This pipeline is intended for use with three levels of proteomics data: total proteome, phosphoproteome, and histone proteome. See Schaefer et al. (2024) for more detail on the total and histone proteome; phosphoproteome data is from Choudhary et al. (2020). Total and phosphoproteome data are designed to be input as an Excel sheet from Proteome Discoverer, and the histone data are the aggregated ratios from EpiProfile (Yuan et al. (2018)).

For the reader, code chunks are collapsible in-text. The full R Markdown source document can also be downloaded from this page by clicking the “Code” dropdown above. Additionally, we have made available the files generated from our data at the project repository on Github. Other files can be downloaded from their respective sources.

knitr::opts_chunk$set(
  echo = TRUE, tidy = "styler", fig.asp = 0.6
)

library(pacman)
p_load(
  EnhancedVolcano, readxl, tidyverse, lintr, Homo.sapiens, DOSE, data.table,
  clusterProfiler, enrichplot, ggvenn, OrganismDbi, eulerr, httpgd, viridis, scales
)

theme_pipeline <- theme_set(theme_gray() + theme(
  plot.title.position = "plot",
  plot.title = element_text(hjust = 0.5)))

1 Proteome data processing

This section is designed to work with normalized abundance data from Proteome Discoverer. The total_path variable should point directly to the Excel file containing the data you would like to use. The range identified in the read_xlsx command should contain columns from “Accession” to the final “Found in Sample:” column.

# Read in normalized abundance data starting with Accession column
total_path <- "~/Documents/R/33060_Control_vs_Infection_JMI_abundances_normalizedabundances.xlsx"
total_KB <- read_xlsx(path = total_path, range = cell_cols(c("D:BI")))

total_KB <- select(
  total_KB,
  c(
    "Accession",
    starts_with(
      c("Abundance Ratio (log2):", "Abundance Ratio Adj. P-Value:", "Found in Sample:")
    )
  )
)
# Retrieve keys and assign symbol to numerical Entrez ID
# (KEGG mapping gives an error if this isn't included)
# Example: given Entrez ID 3043, assign Uniprot accession number P68871,
# then map the gene symbol HBB for easier interpretation later.
reflist <- OrganismDbi::select(
  Homo.sapiens,
  keys = keys(Homo.sapiens, keytype = "UNIPROT"),
  columns = c("ENTREZID", "SYMBOL"), keytype = "UNIPROT"
)
total_KB <- full_join(total_KB, reflist, by = join_by(Accession == UNIPROT))

# Getting rid of this huge variable to save space
rm(reflist)

# Drop invalid data (NA), drop any duplicate cases, rename columns for clarity
total_KB <- total_KB %>%
  drop_na(SYMBOL) %>%
  distinct(SYMBOL, .keep_all = TRUE) %>%
  rename(
    "log2(FC)" = starts_with("Abundance Ratio (log2)"),
    "P value" = starts_with("Abundance Ratio Adj. P-Value")
  )

# Remove points with invalid P values (horizontal line at the top of a plot),
# assign symbols to row names
total_KB <- total_KB %>%
  filter(`P value` > 5.3e-16) %>%
  mutate("SYMBOL2" = `SYMBOL`) %>%
  column_to_rownames(var = "SYMBOL")

# Set threshold values for proteome analysis
total_p_threshold <- 0.05
total_FC_threshold <- 0.5

# Subset the data for plotting later - anything with P < threshold
total_significant <- filter(total_KB, `P value` < total_p_threshold)

1.1 Volcano

Figure 1.1a is a general volcano plot, showing a visualization of the full dataset.

The max.overlaps parameter in the geom_text_repel call should be carefully adjusted. The plot may have to be generated multiple times to get an appropriate labeling count. Setting this value to Inf is possible, but will likely overwhelm the plot and no points will be visible.

# Generating overall volcano plot
# Setting categories for differential expression:
# not significant, P-value only, FC only, both
total_KB$DiffExp <- case_when(
  abs(total_KB$`log2(FC)`) >= total_FC_threshold &
    total_KB$`P value` > total_p_threshold ~ "FC only",
  abs(total_KB$`log2(FC)`) < total_FC_threshold &
    total_KB$`P value` <= total_p_threshold ~ "P val. only",
  abs(total_KB$`log2(FC)`) >= total_FC_threshold &
    total_KB$`P value` <= total_p_threshold ~ "FC and P val.",
  .default = "NS"
)
total_KB$DiffExp <- factor(total_KB$DiffExp,
  levels = c("FC and P val.", "P val. only", "FC only", "NS")
)

# Setting alpha values and labels so the significant points are most visible
total_KB$DEAlpha <- 0.3
total_KB$DEAlpha[abs(total_KB$`log2(FC)`) >= total_FC_threshold &
  total_KB$`P value` <= total_p_threshold] <- 0.8

total_KB$labels <- if_else(
  abs(total_KB$`log2(FC)`) >= total_FC_threshold &
    total_KB$`P value` <= total_p_threshold, total_KB$SYMBOL2, ""
)

ggplot(total_KB, aes(x = `log2(FC)`, y = -log10(`P value`))) +
  geom_point(aes(color = DiffExp, alpha = DEAlpha)) +
  geom_vline(
    xintercept = c(total_FC_threshold, -total_FC_threshold),
    alpha = 0.5, linetype = "dashed"
  ) +
  geom_hline(
    yintercept = -log10(total_p_threshold),
    alpha = 0.5, linetype = "dashed"
  ) +
  geom_text_repel(aes(label = labels), show.legend = FALSE, size = rel(3)) +
  guides(alpha = "none") +
  labs(
    x = "log2(Fold Change)", y = "-log10(P value)",
    title = "Figure 1.1a: Volcano plot",
    color = "Category"
  ) +
  theme(legend.position = "bottom") +
  scale_fill_viridis_d(
    aesthetics = "color", option = "magma",
    begin = 0.2, end = 0.9
  )
## Warning: ggrepel: 239 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

If there are known genes of interest to be included in a plot, Figure 1.1b plots these points on top of the full dataset as a background.

# Defining known genes of interest for a second plot
Group1 <- c(
  "MORF4L1", "SAMSN1", "DTX3L", "MBD3", "HSF1", "EYA3",
  "NCAPD2", "TAF9", "PPP4C", "ZNF638"
)
Group2 <- c("CD276", "CDK6", "CD9", "CD82", "NFKB1", "BST2", "CTSH")

# Assigning shapes and labels for each group in the volcano plot,
# leaving points unlabeled if outside the groups
total_KB$CatShapes <- case_when(
  total_KB$SYMBOL2 %in% Group1 ~ "1",
  total_KB$SYMBOL2 %in% Group2 ~ "2",
  .default = "20"
)

total_KB$CatNames <- case_when(
  total_KB$SYMBOL2 %in% Group1 | total_KB$SYMBOL2 %in% Group2 ~ total_KB$SYMBOL2,
  .default = ""
)
# Generating volcano plot
# arrange() command sorts the named points on top so they aren't buried underneath
ggplot(arrange(total_KB, CatNames), aes(x = `log2(FC)`, y = -log10(`P value`))) +
  geom_point(aes(shape = CatShapes, color = CatShapes)) +
  geom_vline(xintercept = c(total_FC_threshold, -total_FC_threshold), alpha = 0.5, linetype = "dashed") +
  geom_hline(yintercept = -log10(total_p_threshold), alpha = 0.5, linetype = "dashed") +
  geom_text_repel(aes(label = CatNames), size = rel(3), show.legend = FALSE, max.overlaps = Inf) +
  guides(alpha = "none", shape = "none") +
  labs(
    x = "log2(Fold Change)", y = "-log10(P value)",
    title = "Figure 1.1b: Volcano plot with groups"
  ) +
  theme(legend.position = "bottom") +
  scale_fill_viridis_d("Category",
    aesthetics = "color", option = "magma",
    begin = 0.2, end = 0.9,
    labels = c("Group 2", "Group 1", "None")
  )

1.2 Pathway enrichment

This section performs a pathway analysis on the significant proteins alone to reduce computational load when calling enrichGO and enrichKEGG. These variables can be easily adjusted to analyze all proteins together if desired.

In our source dataset, there were no downregulated KEGG pathways. This causes error messages, so the “Total KEGG down” plot has been removed. The call to labels below illustrates a difficulty with plotting long ontology terms, but the label_format parameter is an easy way to solve this. Outside of enrichplot::dotplot, calling label_wrap from labels and passing a character count to wrap at is a good solution. We also show a way to manually replace long names later in this section.

# Separate total_significant subset into upregulated vs downregulated
total_significant_up <- filter(total_significant, `log2(FC)` > 0)
total_significant_down <- filter(total_significant, `log2(FC)` < 0)

# Enriching for *total* GO terms - optional, can be used to visualize overall results
total_GO_up <- enrichGO(total_significant_up$ENTREZID, "org.Hs.eg.db")
total_GO_down <- enrichGO(total_significant_down$ENTREZID, "org.Hs.eg.db")

# Enrich for KEGG pathways
total_KEGG_up <- enrichKEGG(total_significant_up$ENTREZID)
## Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
## Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
total_KEGG_down <- enrichKEGG(total_significant_down$ENTREZID)

# dotplot() can be used for an overview of the relationships between key terms
dotplot(total_KEGG_up,
  showCategory = 20, font.size = rel(1),
  title = "Figure 1.2a: Total KEGG up"
) +
  theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(total_GO_up,
  showCategory = 20, font.size = rel(1),
  title = "Figure 1.2b: Total GO up (unwrapped)"
) +
  theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(total_GO_up,
  showCategory = 20, font.size = rel(1),
  title = "Figure 1.2c: Total GO up (wrapped)", label_format = 60
) +
  theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(total_GO_down,
  showCategory = 20, font.size = rel(1),
  title = "Figure 1.2d: Total GO down"
) +
  theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

For each subset of data, the top 10 terms are found in each GO aspect (molecular function (MF), biological process (BP), cellular component (CC)). To include more terms, adjust the n = 10 parameter in the calls to slice_min().

In this section, we also reword some very long GO terms to fit them in an appropriate-size plot. This list will need to be adjusted based on the enriched terms in an individual dataset.

# Separating the GO terms by ontology aspect
total_GO_up_MF <- enrichGO(total_significant_up$ENTREZID,
  "org.Hs.eg.db",
  ont = "MF"
)
total_GO_up_BP <- enrichGO(total_significant_up$ENTREZID,
  "org.Hs.eg.db",
  ont = "BP"
)
total_GO_up_CC <- enrichGO(total_significant_up$ENTREZID,
  "org.Hs.eg.db",
  ont = "CC"
)
# Taking the top 10 enriched GO terms in each category by P value
total_GO_up_slice_MF <- slice_min(
  filter(total_GO_up_MF@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
total_GO_up_slice_BP <- slice_min(
  filter(total_GO_up_BP@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
total_GO_up_slice_CC <- slice_min(
  filter(total_GO_up_CC@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
# Combining top 10 terms in each aspect
total_GO_up_slice_ALL <- bind_rows(
  "MF" = total_GO_up_slice_MF,
  "BP" = total_GO_up_slice_BP,
  "CC" = total_GO_up_slice_CC,
  .id = "Ontology"
)
total_GO_up_slice_ALL$GR <- parse_ratio(total_GO_up_slice_ALL$GeneRatio)

# Added in to shorten names when plotted - this can be removed or adjusted
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "hydrolase activity, acting on carbon-nitrogen (but not peptide) bonds"] <- "hydrolase, C-N non-peptide bonds"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "hydrolase activity, acting on carbon-nitrogen (but not peptide) bonds, in cyclic amidines"] <- "hydrolase, C-N non-peptide bonds in cyclic amidines"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "antigen processing and presentation of endogenous peptide antigen via MHC class I"] <- "endogenous antigen processing and presentation (MHC class I)"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "antigen processing and presentation of peptide antigen via MHC class I"] <- "peptide antigen processing and presentation (MHC class I)"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "antigen processing and presentation of endogenous peptide antigen"] <- "endogenous antigen processing and presentation"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "exonuclease activity, active with either ribo- or deoxyribonucleic acids and producing 5'-phosphomonoesters"] <- "5'-exonucleases"


# Reordering the top terms by gene ratio
total_GO_up_slice_ALL$Description <- fct_reorder(
  total_GO_up_slice_ALL$Description, total_GO_up_slice_ALL$GR
)
# Repeat for KEGG
# Taking the top 10 enriched terms by P value
total_KEGG_up_slice <- slice_min(filter(total_KEGG_up@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
# Reordering the top terms by gene ratio
total_KEGG_up_slice$GR <- parse_ratio(total_KEGG_up_slice$GeneRatio)
total_KEGG_up_slice$Description <- fct_reorder(
  total_KEGG_up_slice$Description, total_KEGG_up_slice$GR
)
total_KEGG_up_slice$Ontology <- "KEGG"
# Combining GO and KEGG for upregulated proteins
total_up_bound <- bind_rows(
  total_GO_up_slice_ALL, total_KEGG_up_slice
)
total_up_bound$Description <- fct_reorder(
  total_up_bound$Description, total_up_bound$GR
)

# Manually ordering factors to display all GO aspects together
total_up_bound$Ontology <- factor(total_up_bound$Ontology,
  levels = c("BP", "CC", "MF", "KEGG")
)
# Graphing all 4 categories together
ggplot(
  total_up_bound,
  # Gene ratio on X axis, verbose pathway description on Y axis
  # Coloring by adjusted P value and size by the count of genes in a pathway
  aes(x = GR, y = Description, color = p.adjust, size = Count)
) +
  scale_y_discrete(labels = label_wrap(70)) +
  geom_count() +
  labs(
    x = "Gene ratio", y = "Pathway",
    title = "Figure 1.2e: Upregulated pathways"
  ) +
  scale_fill_viridis(
    aesthetics = "color", option = "magma",
    name = "Adjusted P", begin = 0, end = 0.9
  ) +
  scale_shape_manual(values = c(25, 24)) +
  facet_grid(Ontology ~ .,
    drop = TRUE, space = "free_y", scales = "free_y"
  ) +
  theme(
    strip.text = element_text(size = 10),
    panel.spacing = unit(0.6, "lines"),
    axis.text.y = element_text(size = rel(1.1)),
    panel.background = element_rect(fill = "white"),
    panel.border = element_rect(fill = NA, color = "light gray"),
    panel.grid = element_line(color = "gray90")
  )

# Same process as the previous chunk
# Separating the GO terms by ontology aspect
total_GO_down_MF <- enrichGO(total_significant_down$ENTREZID,
  "org.Hs.eg.db",
  ont = "MF"
)
total_GO_down_BP <- enrichGO(total_significant_down$ENTREZID,
  "org.Hs.eg.db",
  ont = "BP"
)
total_GO_down_CC <- enrichGO(total_significant_down$ENTREZID,
  "org.Hs.eg.db",
  ont = "CC"
)
# Taking the top 10 enriched terms in each category by P value
total_GO_down_slice_MF <- slice_min(
  filter(total_GO_down_MF@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
total_GO_down_slice_BP <- slice_min(
  filter(total_GO_down_BP@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
total_GO_down_slice_CC <- slice_min(
  filter(total_GO_down_CC@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)

# Combining top 10 terms in each aspect
total_down_bound <- bind_rows(
  "MF" = total_GO_down_slice_MF,
  "BP" = total_GO_down_slice_BP,
  "CC" = total_GO_down_slice_CC,
  .id = "Ontology"
)
total_down_bound$GR <- parse_ratio(total_down_bound$GeneRatio)

# Reordering the top terms by gene ratio
total_down_bound$Description <- fct_reorder(
  total_down_bound$Description, total_down_bound$GR
)
total_down_bound$Ontology <- factor(total_down_bound$Ontology,
  levels = c("BP", "CC", "MF", "KEGG")
)

# KEGG enrichment did not show anything so it caused an error in our data and has been removed
# If desired, KEGG enrichment can be performed here as in the previous chunk
ggplot(
  total_down_bound,
  aes(x = GR, y = Description, color = p.adjust, size = Count)
) +
  geom_count() +
  labs(
    x = "Gene ratio", y = "Pathway",
    title = "Figure 1.2f: Downregulated pathways"
  ) +
  scale_fill_viridis(
    aesthetics = "color", option = "magma",
    name = "Adjusted P", begin = 0, end = 0.9
  ) +
  scale_shape_manual(values = c(25, 24)) +
  facet_grid(Ontology ~ .,
    drop = TRUE, space = "free_y",
    scales = "free_y"
  ) +
  theme(
    strip.text = element_text(size = 10),
    panel.spacing = unit(0.6, "lines"),
    axis.text.y = element_text(size = rel(1.1)),
    panel.background = element_rect(fill = "white"),
    panel.border = element_rect(fill = NA, color = "light gray"),
    panel.grid = element_line(color = "gray90")
  )

Figure 1.2c clusters terms by function, which shows enriched domains.

# Plotting the enrichment plot for one aspect of upregulated pathways
# Enrich first with pairwise_term_sim, then generate the plot itself
total_GO_up_MF <- pairwise_termsim(total_GO_up_MF)
emapplot(total_GO_up_MF, layout = "nicely", node_label = "none") +
  ggtitle("Figure 1.2g: Upregulated enrichment plot (MF)") +
  geom_text_repel(aes(label = str_wrap(name, width = 60))) +
  theme(
    plot.title.position = "plot", plot.title = element_text(hjust = 0.5),
    plot.margin = margin(1, 1, 1, 1)
  )

2 Phosphoproteome data processing

This section is written to interpret phosphoproteome data. Here, we do not account for particular phosphorylation sites or their functions. The original data is from Choudhary et al. (2020). Below is a reprocessing of this data to align with the total and histone proteome.

# Read in phosphoproteome data, assign symbol to row names
# Already formatted so there's no need to map with reflist
phospho_raw <- read_xlsx("~/Documents/R/PXD_raw.xlsx")
phospho_raw <- phospho_raw %>%
  mutate("SYMBOL2" = `SYMBOL`) %>%
  column_to_rownames(var = "SYMBOL") %>%
  drop_na(SYMBOL2, `log2(FC)`, `P value`) %>%
  distinct(SYMBOL2, .keep_all = TRUE) %>%
  mutate(`...6` = NULL)

# Set threshold values for proteome analysis
phospho_p_threshold <- 0.05
phospho_FC_threshold <- 0.5

phospho_significant <- filter(phospho_raw, `P value` < phospho_p_threshold)

2.1 Volcano

# Repeating the processes from before
# If your groups are too large, they will not display as all labeled at once
Group1_phospho <- c(
  "HJURP", "CHAF1B", "RBL1", "EYA3", "BRCA1",
  "NCOA6", "PALB2", "BARD1", "REPS2", "ZNF511", "BRWD1",
  "L3MBTL3", "WDR76", "CHAF1A", "PIAS2", "RUNX1", "RB1", "JDP2",
  "EP400", "MRGBP", "EMSY", "RYBP", "HIRIP3", "TOPBP1",
  "MGA", "PRDM8", "EZH2", "HDAC5", "DMAP1", "NPAT", "SAP130",
  "SAMD1", "ZMYND8", "ASH1L", "ARID1B", "HDAC4", "BRD9", "NCOR1",
  "MCM3AP", "UTP3", "ZZEF1", "DNTTIP1", "PRKD1", "MTA1", "SATB2",
  "KANSL3", "REST", "SIRT6", "PHC2", "TOPORS", "JMJD1C", "BRD2",
  "ATF7IP", "SP1", "KANSL1", "BMI1", "COMMD3-BMI1", "KMT2C", "ASXL2",
  "TP53BP1", "YY1AP1", "WAC", "BAP1", "L3MBTL2", "ZNF518A", "MBD3",
  "OBI1", "DOT1L", "KDM5C", "RING1", "USP16", "CNOT3", "ATXN1L", "RCOR3",
  "RCOR2", "SCML2", "KMT2D", "ARID1A", "GATAD2B", "ATM", "GTF3C4", "SP140",
  "CHD8", "BPTF", "MECP2", "AHCTF1", "KDM5A", "MYSM1", "ATF7IP2", "NIPBL",
  "LRWD1", "ASH2L", "MARK3", "RRP1B", "EP300", "EHMT2", "TNRC18", "MDC1",
  "RAI1", "CIR1", "FOXO3", "ASXL1", "UBE2O", "KDM2B", "DPF2", "BRD4",
  "NACC1", "CHD4", "TRIM28", "RUVBL1", "ACTL6B", "DDB1", "ACTL6A",
  "STK4", "CARM1", "SUMO3", "ENY2", "CUL5", "PHF5A", "SIRT2", "PKN1",
  "MAPKAPK2", "MAGEA1", "ZNF518B", "SMCHD1", "DLD", "MEN1", "HDAC6",
  "DTX3L", "PSMA8", "H1-6", "PML", "NEK6", "PHF20L1", "RBBP6"
)
Group2_phospho <- c(
  "FANCI", "CDK14", "PRDM1", "ICAM1", "CDK1", "RUNX3", "NOTCH3", "CASP8",
  "BST2", "CTSL", "MX2", "CASP1", "STAT2", "MX1"
)
phospho_raw$DiffExp <- case_when(
  abs(phospho_raw$`log2(FC)`) >= phospho_FC_threshold &
    phospho_raw$`P value` > phospho_p_threshold ~ "FC only",
  abs(phospho_raw$`log2(FC)`) < phospho_FC_threshold &
    phospho_raw$`P value` <= phospho_p_threshold ~ "P val. only",
  abs(phospho_raw$`log2(FC)`) >= phospho_FC_threshold &
    phospho_raw$`P value` <= phospho_p_threshold ~ "FC and P val.",
  .default = "NS"
)
phospho_raw$DiffExp <- factor(phospho_raw$DiffExp,
  levels = c("FC and P val.", "P val. only", "FC only", "NS")
)

# Adding in an alpha value due to point spread and higher hit count
phospho_raw$DEAlpha <- 0.3
phospho_raw$DEAlpha[abs(phospho_raw$`log2(FC)`) >= phospho_FC_threshold &
  phospho_raw$`P value` <= phospho_p_threshold] <- 0.8



# Picking a much smaller subset of points in Group 1 to label just for ease of viewing
Group1_phospho_sub <- c(
  "ASH2L", "ASXL1", "ASXL2", "BAP1", "BMI1", "CHD4", "EZH2", "DMAP1",
  "EP400", "GATAD2B", "KANSL1", "KANSL3", "KMT2C", "KMT2D", "MBD3",
  "MEN1", "MGA", "MRGBP", "MTA1", "NCOA6", "PHC2", "PHF20L1", "REST",
  "RING1", "RUVBL1", "SAP130", "SCML2", "SIRT3", "SIRT6", "EP300"
)
phospho_raw$labels <- if_else(
  abs(phospho_raw$`log2(FC)`) >= phospho_FC_threshold &
    phospho_raw$`P value` <= phospho_p_threshold, phospho_raw$SYMBOL2, ""
)
ggplot(phospho_raw, aes(x = `log2(FC)`, y = -log10(`P value`))) +
  geom_point(aes(color = DiffExp, alpha = DEAlpha)) +
  geom_vline(
    xintercept = c(phospho_FC_threshold, -phospho_FC_threshold),
    alpha = 0.5, linetype = "dashed"
  ) +
  geom_hline(
    yintercept = -log10(phospho_p_threshold),
    alpha = 0.5, linetype = "dashed"
  ) +
  geom_text_repel(aes(label = labels),
    show.legend = FALSE, max.overlaps = 20,
    size = rel(3)
  ) +
  guides(alpha = "none") +
  labs(
    x = "log2(Fold Change)", y = "-log10(P value)",
    title = "Figure 2.1a: Volcano plot",
    color = "Category"
  ) +
  theme(legend.position = "bottom") +
  scale_fill_viridis_d(
    aesthetics = "color", option = "magma",
    begin = 0.2, end = 0.9
  )
## Warning: ggrepel: 1275 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

2.2 Pathway enrichment

# Separate phospho_significant subset into upregulated vs downregulated
phospho_significant_up <- filter(phospho_significant, `log2(FC)` > 0)
phospho_significant_down <- filter(phospho_significant, `log2(FC)` < 0)

# Enriching for *total* GO terms - optional, can be used to visualize overall results
phospho_GO_up <- enrichGO(phospho_significant_up$ENTREZID, "org.Hs.eg.db")
phospho_GO_down <- enrichGO(phospho_significant_down$ENTREZID, "org.Hs.eg.db")

# Enrich for KEGG pathways
phospho_KEGG_up <- enrichKEGG(phospho_significant_up$ENTREZID)
phospho_KEGG_down <- enrichKEGG(phospho_significant_down$ENTREZID)

# dotplot() can be used for an overview of the relationships between key terms
dotplot(phospho_KEGG_up,
  showCategory = 20, font.size = rel(1),
  title = "Figure 2.2a: Phospho KEGG up", label_format = 50
) +
  theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(phospho_KEGG_down,
  showCategory = 20, font.size = rel(1),
  title = "Figure 2.2b: Phospho KEGG down", label_format = 50
) +
  theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(phospho_GO_up,
  showCategory = 20, font.size = rel(1),
  title = "Figure 2.2c: Phospho GO up", label_format = 70
) +
  theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(phospho_GO_down,
  showCategory = 20, font.size = rel(1),
  title = "Figure 2.2d: Phospho GO down", label_format = 50
) +
  theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

# Separating the GO terms by ontology aspect
phospho_GO_up_MF <- enrichGO(phospho_significant_up$ENTREZID,
  "org.Hs.eg.db",
  ont = "MF"
)
phospho_GO_up_BP <- enrichGO(phospho_significant_up$ENTREZID,
  "org.Hs.eg.db",
  ont = "BP"
)
phospho_GO_up_CC <- enrichGO(phospho_significant_up$ENTREZID,
  "org.Hs.eg.db",
  ont = "CC"
)
# Taking the top 10 enriched terms in each category by P value
phospho_GO_up_slice_MF <- slice_min(
  filter(phospho_GO_up_MF@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
phospho_GO_up_slice_BP <- slice_min(
  filter(phospho_GO_up_BP@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
phospho_GO_up_slice_CC <- slice_min(
  filter(phospho_GO_up_CC@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)

# Combining top 10 terms in each aspect
phospho_GO_up_slice_ALL <- bind_rows(
  "MF" = phospho_GO_up_slice_MF,
  "BP" = phospho_GO_up_slice_BP,
  "CC" = phospho_GO_up_slice_CC,
  .id = "Ontology"
)
phospho_GO_up_slice_ALL$GR <- parse_ratio(phospho_GO_up_slice_ALL$GeneRatio)

# Added in to shorten names when plotted - this can be removed or adjusted
phospho_GO_up_slice_ALL["Description"][phospho_GO_up_slice_ALL["Description"] == "oxidoreductase activity, acting on the aldehyde or oxo group of donors, NAD or NADP as acceptor"] <- "oxidoreductase, aldehyde/oxo donor, NAD/NADP acceptor"

# Reordering the top terms by gene ratio
phospho_GO_up_slice_ALL$Description <- fct_reorder(
  phospho_GO_up_slice_ALL$Description, phospho_GO_up_slice_ALL$GR
)
# Repeat for KEGG
# Taking the top 10 enriched terms by P value
phospho_KEGG_up_slice <- slice_min(
  filter(phospho_KEGG_up@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)

# Reordering the top terms by gene ratio
phospho_KEGG_up_slice$GR <- parse_ratio(phospho_KEGG_up_slice$GeneRatio)
phospho_KEGG_up_slice$Description <- fct_reorder(
  phospho_KEGG_up_slice$Description,
  phospho_KEGG_up_slice$GR
)
phospho_KEGG_up_slice$Ontology <- "KEGG"
# Combining GO and KEGG for upregulated proteins
phospho_up_bound <- bind_rows(
  phospho_GO_up_slice_ALL, phospho_KEGG_up_slice
)
phospho_up_bound$Description <- fct_reorder(
  phospho_up_bound$Description, phospho_up_bound$GR
)

# Manually ordering factors to display all GO aspects together
phospho_up_bound$Ontology <- factor(phospho_up_bound$Ontology,
  levels = c("BP", "CC", "MF", "KEGG")
)
# Graphing all 4 categories together
ggplot(
  phospho_up_bound,
  # Gene ratio on X axis, verbose pathway description on Y axis
  # Coloring by adjusted P value and size by the count of genes in a pathway
  aes(x = GR, y = Description, color = p.adjust, size = Count)
) +
  scale_y_discrete(labels = label_wrap(80)) +
  geom_count() +
  labs(
    x = "Gene ratio", y = "Pathway",
    title = "Figure 2.2e: Upregulated pathways"
  ) +
  scale_fill_viridis(
    aesthetics = "color", option = "magma",
    name = "Adjusted P", begin = 0, end = 0.9
  ) +
  scale_shape_manual(values = c(25, 24)) +
  facet_grid(Ontology ~ .,
    drop = TRUE, space = "free_y",
    scales = "free_y"
  ) +
  theme(
    strip.text = element_text(size = 10),
    panel.spacing = unit(0.6, "lines"),
    axis.text.y = element_text(size = rel(1.1)),
    panel.background = element_rect(fill = "white"),
    panel.border = element_rect(fill = NA, color = "light gray"),
    panel.grid = element_line(color = "gray90")
  )

# Same process as the previous chunks
# Separating the GO terms by ontology aspect
phospho_GO_down_MF <- enrichGO(phospho_significant_down$ENTREZID,
  "org.Hs.eg.db",
  ont = "MF"
)
phospho_GO_down_BP <- enrichGO(phospho_significant_down$ENTREZID,
  "org.Hs.eg.db",
  ont = "BP"
)
phospho_GO_down_CC <- enrichGO(phospho_significant_down$ENTREZID,
  "org.Hs.eg.db",
  ont = "CC"
)
# Taking the top 10 enriched terms in each category by P value
phospho_GO_down_slice_MF <- slice_min(
  filter(phospho_GO_down_MF@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
phospho_GO_down_slice_BP <- slice_min(
  filter(phospho_GO_down_BP@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)
phospho_GO_down_slice_CC <- slice_min(
  filter(phospho_GO_down_CC@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)

# Combining top 10 terms in each aspect
phospho_down_bound <- bind_rows(
  "MF" = phospho_GO_down_slice_MF,
  "BP" = phospho_GO_down_slice_BP,
  "CC" = phospho_GO_down_slice_CC,
  .id = "Ontology"
)
# Repeat for KEGG
# Taking the top 10 enriched terms by P value
phospho_KEGG_down_slice <- slice_min(
  filter(phospho_KEGG_down@result, p.adjust < 0.05),
  order_by = `p.adjust`, n = 10
)

# Reordering the top terms by gene ratio
phospho_KEGG_down_slice$GR <- parse_ratio(phospho_KEGG_down_slice$GeneRatio)
phospho_KEGG_down_slice$Description <- fct_reorder(
  phospho_KEGG_down_slice$Description,
  phospho_KEGG_down_slice$GR
)
phospho_KEGG_down_slice$Ontology <- "KEGG"
# Combining GO and KEGG for downregulated proteins
phospho_down_bound <- bind_rows(
  phospho_down_bound, phospho_KEGG_down_slice
)
phospho_down_bound$GR <- parse_ratio(phospho_down_bound$GeneRatio)

phospho_down_bound$Description <- fct_reorder(
  phospho_down_bound$Description, phospho_down_bound$GR
)

# Manually ordering factors to display all GO aspects together
phospho_down_bound$Ontology <- factor(phospho_down_bound$Ontology,
  levels = c("BP", "CC", "MF", "KEGG")
)
# Graphing all 4 categories together
ggplot(
  phospho_down_bound,
  # Gene ratio on X axis, verbose pathway description on Y axis
  # Coloring by adjusted P value and size by the count of genes in a pathway
  aes(x = GR, y = Description, color = p.adjust, size = Count)
) +
  scale_y_discrete(labels = label_wrap(70)) +
  geom_count() +
  labs(
    x = "Gene ratio", y = "Pathway",
    title = "Figure 2.2f: Downregulated pathways"
  ) +
  scale_fill_viridis(
    aesthetics = "color", option = "magma",
    name = "Adjusted P", begin = 0, end = 0.9
  ) +
  scale_shape_manual(values = c(25, 24)) +
  facet_grid(Ontology ~ .,
    drop = TRUE, space = "free_y",
    scales = "free_y"
  ) +
  theme(
    strip.text = element_text(size = 10),
    panel.spacing = unit(0.6, "lines"),
    axis.text.y = element_text(size = rel(1.1)),
    panel.background = element_rect(fill = "white"),
    panel.border = element_rect(fill = NA, color = "light gray"),
    panel.grid = element_line(color = "gray90")
  )

phospho_GO_up_MF <- pairwise_termsim(phospho_GO_up_MF)
emapplot(phospho_GO_up_MF, layout = "nicely", node_label = "none") +
  geom_text_repel(aes(label = str_wrap(name, width = 60))) +
  ggtitle("Figure 2.2g: Upregulated enrichment plot (phospho, MF)") +
  theme(
    plot.title.position = "plot", plot.title = element_text(hjust = 0.5),
    plot.margin = margin(1, 1, 1, 1)
  )

phospho_GO_down_MF <- pairwise_termsim(phospho_GO_down_MF)
emapplot(phospho_GO_down_MF, layout = "nicely", node_label = "none") +
  geom_text_repel(aes(label = str_wrap(name, width = 60))) +
  ggtitle("Figure 2.2h: Downregulated enrichment plot (phospho, MF)") +
  theme(
    plot.title.position = "plot", plot.title = element_text(hjust = 0.5),
    plot.margin = margin(1, 1, 1, 1)
  )

3 Histone processing

This section processes data output from EpiProfile. We choose to assess both tail fragments and individual summed PTMs to indicate co-occurrence, which may signify activity of certain epigenetic actors. Note that the exported .xls files will have to be opened manually one time, then saved again as .xls(x) format due to the output format. Into the range argument, pass the cell location of the first peptide name (“H3_3_8 unmod” in our data) to set the top left corner, then (NA, [target col]) as the second location to set the bottom right corner. This section assumes the data is separating two conditions, infected and control.

histone_ratios_path <- "~/Documents/R/histone_ratios/histone_ratios.xls"
histone_single_path <- "~/Documents/R/histone_ratios/histone_ratios_single_PTMs.xls"

# The columns will have to be manually parsed - include the names (first column) and ratios for all samples
histone_frag_raw <- drop_na(read_excel(path = histone_ratios_path, range = cell_limits(c(4, 1), c(NA, 11)), col_names = c("PTM", "C1", "I1", "C2", "I2", "C3", "I3", "C4", "I4", "C5", "I5")))

histone_single_raw <- read_excel(path = histone_single_path, col_names = c("PTM", "C1", "I1", "C2", "I2", "C3", "I3", "C4", "I4", "C5", "I5"), range = cell_limits(c(2, 1), c(NA, 11)))

# Set threshold values for proteome analysis
histone_p_threshold <- 0.05
histone_FC_threshold <- 0.5

3.1 Fragments

Name cleanup is not strictly necessary but makes labels easier to read. There may also be some long amino acid strings such as “H1 1-35 H12.SETAPAAPAAAPPAEKAPVKKKAAKKAGGTPR” that indicate a unique isoform (H1.2 in this case). These can also be modified by calling str_replace_all as desired.

# Cleaning up names
ratios_frag <- histone_frag_raw
ratios_frag$PTM <- ratios_frag$PTM %>%
  str_replace_all("H3_3_8", "H3 3-8") %>%
  str_replace_all("H3_9_17", "H3 9-17") %>%
  str_replace_all("H3_18_26", "H3 18-26") %>%
  str_replace_all("H3_27_40", "H3 27-40") %>%
  str_replace_all("H33_27_40", "H3.3 27-40") %>%
  str_replace_all("H3_41_49", "H3 41-49") %>%
  str_replace_all("H3_54_63", "H3 54-63") %>%
  str_replace_all("H3_73_83", "H3 73-83") %>%
  str_replace_all("H3_117_128", "H3 117-128") %>%
  str_replace_all("H4_4_17", "H4 4-17") %>%
  str_replace_all("H4_20_23", "H4 20-23") %>%
  str_replace_all("H4_24_35", "H4 24-35") %>%
  str_replace_all("H4_40_45", "H4 40-45") %>%
  str_replace_all("H4_79_92", "H4 79-92") %>%
  str_replace_all("H1_1_35", "H1 1-35") %>%
  str_replace_all("H1_54_81", "H1 54-71") %>%
  str_replace_all("H2A1_36_42", "H2A1 36-42") %>%
  str_replace_all("H2A3_36_42", "H2A3 36-42") %>%
  str_replace_all("H2AX_36_42", "H2AX 36-42") %>%
  str_replace_all("H2A1_4_11", "H2A1 4-11") %>%
  str_replace_all("H2AJ_4_11", "H2AJ 4-11") %>%
  str_replace_all("H2AX_4_11", "H2AX 4-11") %>%
  str_replace_all("H2A1_1_11", "H2A1 1-11") %>%
  str_replace_all("H2AV_1_19", "H2AV 1-19") %>%
  str_replace_all("H2AZ_1_19", "H2AZ 1-19") %>%
  str_replace_all("H2A1_12_17", "H2A1 12-17") %>%
  str_replace_all("H2A3_12_17", "H2A3 12-17") %>%
  str_replace_all("H2A1_72_77", "H2A1 72-77") %>%
  str_replace_all("H2A_82_88", "H2A 82-88") %>%
  str_replace_all("H2A_1_88", "H2A 1-88") %>%
  str_replace_all("H2B_1_29", "H2B 1-29")

# Group two conditions together
ratios_frag <- ratios_frag %>%
  relocate(contains("I"), .after = last_col()) %>%
  relocate("PTM")

# Calculate infected/control fold change and P value
ratios_frag <- ratios_frag %>%
  rowwise() %>%
  mutate(
    c_mean = mean(c_across("C1":"C5")),
    i_mean = mean(c_across("I1":"I5")),
    FC = c_across(i_mean) / c_across(c_mean),
    "log2(FC)" = log2(FC),
    is_constant = all(c_across("C1":"C5") == c_across("I1":"I5"))
  )

ratios_frag <- column_to_rownames(ratios_frag, var = "PTM")
ratios_frag$PTM <- rownames(ratios_frag)

ratios_frag$"P value" <- NA
ratios_p <- ratios_frag %>%
  rowwise() %>%
  filter(is_constant == FALSE) %>%
  # Removing any proteins that have all equal values, which gives an
  # error when running t.test()
  mutate("P value" = t.test(c_across("C1":"C5"), c_across("I1":"I5"),
    alternative = "two.sided", var.equal = TRUE
  )$p.value)

ratios_frag <- full_join(ratios_frag, ratios_p)
## Joining with `by = join_by(C1, C2, C3, C4, C5, I1, I2, I3, I4, I5, c_mean,
## i_mean, FC, `log2(FC)`, is_constant, PTM, `P value`)`
ratios_frag$`log2(FC)`[ratios_frag$`log2(FC)` == "NaN"] <- NA
ratios_frag$FC[ratios_frag$FC == "NaN"] <- NA

To address infinite fold change values that appear when a protein is not detected in one condition, we set them to have a unique shape in the final plot and assign a fold change value equal to 1.5 times the maximum valid value.

# Generating overall volcano plot
# Setting categories for differential expression:
# not significant, P-value only, FC only, both
ratios_frag$DiffExp <- case_when(
  abs(ratios_frag$`log2(FC)`) >= histone_FC_threshold &
    ratios_frag$`P value` > histone_p_threshold ~ "FC only",
  abs(ratios_frag$`log2(FC)`) < histone_FC_threshold &
    ratios_frag$`P value` <= histone_p_threshold ~ "P val. only",
  abs(ratios_frag$`log2(FC)`) >= histone_FC_threshold &
    ratios_frag$`P value` <= histone_p_threshold ~ "FC and P val.",
  .default = "NS"
)
ratios_frag$DiffExp <- factor(ratios_frag$DiffExp,
  levels = c("FC and P val.", "P val. only", "FC only", "NS")
)

ratios_frag$labels <- if_else(
  abs(ratios_frag$`log2(FC)`) >= histone_FC_threshold &
    ratios_frag$`P value` <= histone_p_threshold, ratios_frag$PTM, ""
)

ratios_frag_plot <- ratios_frag %>%
  drop_na() %>%
  filter(`log2(FC)` != "-Inf" & `log2(FC)` != "Inf")
ratios_frag_plot$shape <- "16"

ratios_frag_invalid <- ratios_frag %>%
  drop_na() %>%
  filter(`log2(FC)` == "-Inf" | `log2(FC)` == "Inf")
ratios_frag_invalid$`log2(FC)`[ratios_frag_invalid$`log2(FC)` == "-Inf"] <- min(ratios_frag_plot$`log2(FC)`) * 1.5
ratios_frag_invalid$`log2(FC)`[ratios_frag_invalid$`log2(FC)` == "Inf"] <- max(ratios_frag_plot$`log2(FC)`) * 1.5
ratios_frag_invalid$shape <- "17"

ratios_frag_plot <- full_join(ratios_frag_plot, ratios_frag_invalid)
## Joining with `by = join_by(C1, C2, C3, C4, C5, I1, I2, I3, I4, I5, c_mean,
## i_mean, FC, `log2(FC)`, is_constant, PTM, `P value`, DiffExp, labels, shape)`
ggplot(ratios_frag_plot, aes(x = `log2(FC)`, y = -log10(`P value`))) +
  geom_point(aes(color = DiffExp, shape = shape)) +
  expand_limits(x = c(min(ratios_frag_plot$`log2(FC)`), max(ratios_frag_plot$`log2(FC)`))) +
  geom_vline(
    xintercept = c(histone_FC_threshold, -histone_FC_threshold),
    alpha = 0.5, linetype = "dashed"
  ) +
  geom_hline(
    yintercept = -log10(histone_p_threshold),
    alpha = 0.5, linetype = "dashed"
  ) +
  geom_text_repel(aes(label = labels),
    show.legend = FALSE, max.overlaps = 20,
    size = rel(3)
  ) +
  guides(shape = "none") +
  labs(
    x = "log2(Fold Change)", y = "-log10(P value)",
    title = "Figure 3.1a: Fragment volcano plot",
    color = "Category"
  ) +
  theme(legend.position = "bottom") +
  scale_fill_viridis_d(
    aesthetics = "color", option = "magma",
    begin = 0.2, end = 0.9
  )

3.2 Single

# Cleaning up names
ratios_single <- histone_single_raw
ratios_single$PTM <- ratios_single$PTM %>%
  str_replace_all("H31", "H3.1") %>%
  str_replace_all("H33", "H3.3")

# Group two conditions together
ratios_single <- ratios_single %>%
  relocate(contains("I"), .after = last_col()) %>%
  relocate("PTM")

# Calculate infected/control fold change and P value
ratios_single <- ratios_single %>%
  rowwise() %>%
  mutate(
    c_mean = mean(c_across("C1":"C5")),
    i_mean = mean(c_across("I1":"I5")),
    FC = c_across(i_mean) / c_across(c_mean),
    "log2(FC)" = log2(FC),
    "P value" = t.test(c_across("C1":"C5"), c_across("I1":"I5"),
      alternative = "two.sided", var.equal = TRUE
    )$p.value
  )
ratios_single <- column_to_rownames(ratios_single, var = "PTM")
ratios_single$PTM <- rownames(ratios_single)

ratios_single <- ratios_single %>%
  rowwise() %>%
  mutate(
    c_mean = mean(c_across("C1":"C5")),
    i_mean = mean(c_across("I1":"I5")),
    FC = c_across(i_mean) / c_across(c_mean),
    "log2(FC)" = log2(FC),
    is_constant = all(c_across("C1":"C5") == c_across("I1":"I5"))
  )

ratios_single <- column_to_rownames(ratios_single, var = "PTM")
ratios_single$PTM <- rownames(ratios_single)

ratios_single$"P value" <- NA
single_p <- ratios_single %>%
  rowwise() %>%
  filter(is_constant == FALSE) %>%
  # Removing any proteins that have all equal values, which gives an
  # error when running t.test()
  mutate("P value" = t.test(c_across("C1":"C5"), c_across("I1":"I5"),
    alternative = "two.sided", var.equal = TRUE
  )$p.value)

ratios_single <- full_join(ratios_single, single_p)
## Joining with `by = join_by(C1, C2, C3, C4, C5, I1, I2, I3, I4, I5, c_mean,
## i_mean, FC, `log2(FC)`, `P value`, is_constant, PTM)`
ratios_single$`log2(FC)`[ratios_single$`log2(FC)` == "NaN"] <- NA
ratios_single$FC[ratios_single$FC == "NaN"] <- NA
# Generating overall volcano plot - same as previous
# Setting categories for differential expression:
# not significant, P-value only, FC only, both
ratios_single$DiffExp <- case_when(
  abs(ratios_single$`log2(FC)`) >= histone_FC_threshold &
    ratios_single$`P value` > histone_p_threshold ~ "FC only",
  abs(ratios_single$`log2(FC)`) < histone_FC_threshold &
    ratios_single$`P value` <= histone_p_threshold ~ "P val. only",
  abs(ratios_single$`log2(FC)`) >= histone_FC_threshold &
    ratios_single$`P value` <= histone_p_threshold ~ "FC and P val.",
  .default = "NS"
)
ratios_single$DiffExp <- factor(ratios_single$DiffExp,
  levels = c("FC and P val.", "P val. only", "FC only", "NS")
)

# Setting labels
ratios_single$labels <- if_else(
  abs(ratios_single$`log2(FC)`) >= histone_FC_threshold &
    ratios_single$`P value` <= histone_p_threshold, ratios_single$PTM, ""
)

ratios_single_plot <- ratios_single %>%
  drop_na() %>%
  filter(`log2(FC)` != "-Inf" & `log2(FC)` != "Inf")
ratios_single_plot$shape <- "16"

ratios_single_invalid <- ratios_single %>%
  drop_na() %>%
  filter(`log2(FC)` == "-Inf" | `log2(FC)` == "Inf")
ratios_single_invalid$`log2(FC)`[ratios_single_invalid$`log2(FC)` == "-Inf"] <- min(ratios_single_plot$`log2(FC)`) * 1.5
ratios_single_invalid$`log2(FC)`[ratios_single_invalid$`log2(FC)` == "Inf"] <- max(ratios_single_plot$`log2(FC)`) * 1.5
ratios_single_invalid$shape <- "17"

ratios_single_plot <- full_join(ratios_single_plot, ratios_single_invalid)
## Joining with `by = join_by(C1, C2, C3, C4, C5, I1, I2, I3, I4, I5, c_mean,
## i_mean, FC, `log2(FC)`, `P value`, is_constant, PTM, DiffExp, labels, shape)`
ggplot(ratios_single_plot, aes(x = `log2(FC)`, y = -log10(`P value`))) +
  geom_point(aes(color = DiffExp, shape = shape)) +
  expand_limits(x = c(min(ratios_single_plot$`log2(FC)`), max(ratios_single_plot$`log2(FC)`))) +
  geom_vline(
    xintercept = c(histone_FC_threshold, -histone_FC_threshold),
    alpha = 0.5, linetype = "dashed"
  ) +
  geom_hline(
    yintercept = -log10(histone_p_threshold),
    alpha = 0.5, linetype = "dashed"
  ) +
  geom_text_repel(aes(label = labels),
    show.legend = FALSE, max.overlaps = 20,
    size = rel(3)
  ) +
  guides(shape = "none") +
  labs(
    x = "log2(Fold Change)", y = "-log10(P value)",
    title = "Figure 3.1b: Net histone volcano plot",
    color = "Category"
  ) +
  theme(legend.position = "bottom") +
  scale_fill_viridis_d(
    aesthetics = "color", option = "magma",
    begin = 0.2, end = 0.9
  )

4 Differential expression

Here we begin a comparative analysis of the two larger datasets (total proteome and phosphoproteome).

# Venn diagram to quickly look at proteome/phosphoproteome overlap
venn_lists <- list_to_data_frame(list("Set 1" = total_KB$SYMBOL2, "Set 2" = phospho_raw$SYMBOL2))
ggvenn(venn_lists, set_name_size = 5) +
  ggtitle("Figure 4a: Set overlap") +
  theme(plot.margin = margin(1, 1, 1, 2))

4.1 Total proteome infected vs control

Section 4.1 directly compares the total proteome to itself. Here, we examine the sets of proteins identified exclusively in infected cells (identified by columns with “JMI”), control cells, or both. Detection inputs for these columns can be “High”, “Peak Found”, or “n/a”. To identify a protein found only in one condition, we check that at least one replicate from that condition is not “n/a”. We then check that, simultaneously, all replicates from the other condition are equal to “n/a”, indicating the protein was not found. If both requirements are met, a string is mapped to that protein indicating its presence. If a protein has at least one replicate not equal to “n/a” in both experimental conditions, “All” is assigned, indicating overlap.

# Identify proteins expressed in both conditions, only control, or only infected.
total_diff_valid <- total_KB %>% mutate(`Differential?` = case_when(
  # Control ONLY
  # BCG empty, at least 1 ctrl not empty
  (((`Found in Sample: F1: Sample, Control` != "n/a") |
    (`Found in Sample: F2: Sample, Control` != "n/a") |
    (`Found in Sample: F3: Sample, Control` != "n/a")) &
    ((`Found in Sample: F4: Sample, JMI` == "n/a") &
      (`Found in Sample: F5: Sample, JMI` == "n/a") &
      (`Found in Sample: F5: Sample, JMI` == "n/a") &
      (`Found in Sample: F6: Sample, JMI` == "n/a") &
      (`Found in Sample: F7: Sample, JMI` == "n/a") &
      (`Found in Sample: F8: Sample, JMI` == "n/a"))) ~ "Control only",
  # Infected ONLY
  # Control empty, at least one BCG not empty
  (((`Found in Sample: F1: Sample, Control` == "n/a") &
    (`Found in Sample: F2: Sample, Control` == "n/a") &
    (`Found in Sample: F3: Sample, Control` == "n/a")) &
    ((`Found in Sample: F4: Sample, JMI` != "n/a") |
      (`Found in Sample: F5: Sample, JMI` != "n/a") |
      (`Found in Sample: F5: Sample, JMI` != "n/a") |
      (`Found in Sample: F6: Sample, JMI` != "n/a") |
      (`Found in Sample: F7: Sample, JMI` != "n/a") |
      (`Found in Sample: F8: Sample, JMI` != "n/a"))) ~ "BCG only",
  # Both
  .default = "All"
))

Euler plots are sized proportionally to their groups and are more friendly to multiple sets. To generate the Euler plots, the input needs to be a single column containing all peptides found in that condition. The print command provides summary statistics to ensure that the Euler plot does not misrepresent the data.

# Pivoting to make a wider table
total_wider_valid <- pivot_wider(total_diff_valid,
  names_from = `Differential?`, values_from = `SYMBOL2`
)

# Create a list of infection only peptides, removing empty rows (NA)
BCG_dist_valid <- total_wider_valid %>%
  distinct(`BCG only`) %>%
  drop_na()
colnames(BCG_dist_valid) <- c("Peptides")

# Create a list of control only peptides, removing empty rows (NA)
Ctrl_dist_valid <- total_wider_valid %>%
  distinct(`Control only`) %>%
  drop_na()
colnames(Ctrl_dist_valid) <- c("Peptides")

# Create a list of peptides present in both, removing empty rows (NA)
All_dist_valid <- total_wider_valid %>%
  distinct(`All`) %>%
  drop_na()
colnames(All_dist_valid) <- c("Peptides")

# Create a list of BCG + both
BCG_both_dist_valid <- add_row(BCG_dist_valid, All_dist_valid) %>% distinct()
colnames(BCG_both_dist_valid) <- c("Infected")

# Create a list of control + both
Ctrl_both_dist_valid <- add_row(Ctrl_dist_valid, All_dist_valid) %>% distinct()
colnames(Ctrl_both_dist_valid) <- c("Control")

# Generate plot
fit_total_valid <- euler(c(BCG_both_dist_valid, Ctrl_both_dist_valid))
plot(fit_total_valid,
  quantities = TRUE,
  fills = c("palevioletred1", "cornflowerblue", "gainsboro"),
  main = "Figure 4.1a: Condition Euler plot"
)

print(fit_total_valid)
##                  original fitted residuals regionError
## Infected               29     29         0           0
## Control                20     20         0           0
## Infected&Control     3174   3174         0           0
## 
## diagError: 0 
## stress:    0

5 Disease enrichment

To complete the integrated analysis, we examine a gene-disease association (GDA) summary from DisGeNET. The GDA Excel sheets can be downloaded directly from the website after identifying the disease of interest. Point the read_xlsx call to this file.

BC_xl <- read_xlsx("~/Documents/R/Final analysis/C2145472_disease_gda_summary.xlsx")

# Get lists of proteins that were significant AND above the FC threshold
total_FC <- filter(total_significant, abs(`log2(FC)`) > total_FC_threshold)
phospho_FC <- filter(phospho_significant, abs(`log2(FC)`) > phospho_FC_threshold)

# Generate plot
ggvenn(list(
  "NMIBC" = BC_xl$Gene,
  "Total proteome" = total_FC$SYMBOL2,
  "Phosphoproteome" = phospho_FC$SYMBOL2
)) + ggtitle("Figure 5a: Venn diagram") + theme(
  plot.title.position = "plot",
  plot.title = element_text(hjust = 0.5)
)

fit_BC <- euler(list(
  "NMIBC" = BC_xl$Gene,
  "Total proteome" = total_FC$SYMBOL2,
  "Phosphoproteome" = phospho_FC$SYMBOL2
))
plot(fit_BC, quantities = TRUE, main = "Figure 5a: GDA Euler plot")

print(list_to_data_frame(fit_BC))
## # A tibble: 38 × 8
##       key ellipses original.values fitted.values residuals regionError diagError
##     <dbl> <lgl>    <lgl>           <lgl>         <lgl>     <lgl>       <lgl>    
##  1  -9.75 FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
##  2  10.4  FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
##  3   5.20 FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
##  4 -16.2  FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
##  5 -16.2  FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
##  6   7.93 FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
##  7  11.9  FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
##  8   9.59 FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
##  9  20.5  FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
## 10   1.85 FALSE    FALSE           FALSE         FALSE     FALSE       FALSE    
## # ℹ 28 more rows
## # ℹ 1 more variable: stress <lgl>

From this table, there is a missing overlap region in all three sets that the Euler diagram could not resolve, as indicated by the non-zero residuals. In this case, the Venn diagram is the better choice.

# Print the list of proteins in each intersection
cat(paste("**NMIBC/Phospho:** ", paste(intersect(BC_xl$Gene, phospho_FC$SYMBOL2), collapse = ", ")))

NMIBC/Phospho: MKI67, EZH2, TGFBI, EIF4EBP1, PSMD9, NUMA1, TMED7-TICAM2, TMED7, ARID1A, UVRAG, DHCR24, SULT1A1, RAF1, SIRT6, PKM, CMAS, ATP7A, BAP1, CD163, KIF20B, HDAC4, CDC20, RING1, BST2, TP53BP1, TSC2, ENG, DECR1, EPHB2, SATB2, CEACAM5, HDAC6, HDAC5, CRYZ, CSTF2, CHEK2, NACC1, ATM, NFATC1, NOTCH3, HLA-A, HMOX1, GSTM1, KPNA2

cat(paste("**NMIBC/Total:** ", paste(intersect(BC_xl$Gene, total_FC$SYMBOL2), collapse = ", ")))

NMIBC/Total: ZNRD2, DCTN6, MMP9, SULT1A1, PECAM1, BST2, TLR2, TARBP2, NFKB1, HMOX1

cat(paste("**Total/Phospho:** ", paste(intersect(total_FC$SYMBOL2, phospho_FC$SYMBOL2), collapse = ", ")))

Total/Phospho: OSBPL11, PARP14, DERL1, LSM3, OAS1, SNRPG, STAT2, INPP5F, RNF213, SEPHS1, FKBP8, IFI16, MOB1B, ALDH2, BST2, SLFN5, UBE2L6, VKORC1, DDI2, EYA3, LYSMD2, RRM1, PKIB, HMOX1, SIRPA, CMPK2, HELZ2, TAP1, TAP2, SNRPB, EIF2AK2, UBE2D3, TAPBP, DTX3L, CNP, SIRT2, TOR1AIP2, IFI44, SULT1A1, HMBS, SRSF10, FTL, ANKZF1, SERPINB1, PNPT1, PML, PPP1R18, ATG16L1, MAT2B, GRN, MTMR2, CHMP7, ACOX1, PEA15, NUP37, RAB13, RTN1, LIPA, MBD3, EMILIN2, RBBP6

cat(paste("**NMIBC/Phospho/Total:** ", paste(intersect(intersect(BC_xl$Gene, phospho_FC$SYMBOL2), total_FC$SYMBOL2), collapse = ", ")))

NMIBC/Phospho/Total: SULT1A1, BST2, HMOX1

Choudhary, Eira, C. Korin Bullen, Renu Goel, Alok Kumar Singh, Monali Praharaj, Preeti Thakur, Rohan Dhiman, William R. Bishai, and Nisheeth Agarwal. 2020. “Relative and Quantitative Phosphoproteome Analysis of Macrophages in Response to Infection by Virulent and Avirulent Mycobacteria Reveals a Distinct Role of the Cytosolic RNA Sensor RIG-I in Mycobacterium Tuberculosis Pathogenesis.” Journal of Proteome Research 19 (6): 2316–36. https://doi.org/10.1021/acs.jproteome.9b00895.
Schaefer, Zoe, John Iradukunda, Evelyn N. Lumngwena, Kari B. Basso, Jonathan M. Blackburn, and Ivana K. Parker. 2024. “Multilevel Proteomics Reveals Epigenetic Signatures in BCG-Mediated Macrophage Activation.” Molecular & Cellular Proteomics 23 (11): 100851. https://doi.org/10.1016/j.mcpro.2024.100851.
Yuan, Zuo-Fei, Simone Sidoli, Dylan M. Marchione, Johayra Simithy, Kevin A. Janssen, Mary R. Szurgot, and Benjamin A. Garcia. 2018. “EpiProfile 2.0: A Computational Platform for Processing Epi-Proteomics Mass Spectrometry Data.” Journal of Proteome Research 17 (7): 2533–41. https://doi.org/10.1021/acs.jproteome.8b00133.
LS0tCnRpdGxlOiAiTXVsdGktb21pY3MgZGF0YSBhbmFseXNpcyIKYXV0aG9yOiAiWm9lIFNjaGFlZmVyIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgZGZfcHJpbnQ6IGthYmxlCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWIKLS0tCgpUaGlzIHBpcGVsaW5lIGlzIGludGVuZGVkIGZvciB1c2Ugd2l0aCB0aHJlZSBsZXZlbHMgb2YgcHJvdGVvbWljcyBkYXRhOiB0b3RhbCBwcm90ZW9tZSwgcGhvc3Bob3Byb3Rlb21lLCBhbmQgaGlzdG9uZSBwcm90ZW9tZS4gU2VlIEBTY2hhZWZlcjIwMjQgZm9yIG1vcmUgZGV0YWlsIG9uIHRoZSB0b3RhbCBhbmQgaGlzdG9uZSBwcm90ZW9tZTsgcGhvc3Bob3Byb3Rlb21lIGRhdGEgaXMgZnJvbSBAY2hvdWRoYXJ5MjAyMC4gVG90YWwgYW5kIHBob3NwaG9wcm90ZW9tZSBkYXRhIGFyZSBkZXNpZ25lZCB0byBiZSBpbnB1dCBhcyBhbiBFeGNlbCBzaGVldCBmcm9tIFByb3Rlb21lIERpc2NvdmVyZXIsIGFuZCB0aGUgaGlzdG9uZSBkYXRhIGFyZSB0aGUgYWdncmVnYXRlZCByYXRpb3MgZnJvbSBFcGlQcm9maWxlIChAWXVhbjIwMTgpLgoKRm9yIHRoZSByZWFkZXIsIGNvZGUgY2h1bmtzIGFyZSBjb2xsYXBzaWJsZSBpbi10ZXh0LiBUaGUgZnVsbCBSIE1hcmtkb3duIHNvdXJjZSBkb2N1bWVudCBjYW4gYWxzbyBiZSBkb3dubG9hZGVkIGZyb20gdGhpcyBwYWdlIGJ5IGNsaWNraW5nIHRoZSAiQ29kZSIgZHJvcGRvd24gYWJvdmUuIEFkZGl0aW9uYWxseSwgd2UgaGF2ZSBtYWRlIGF2YWlsYWJsZSB0aGUgZmlsZXMgZ2VuZXJhdGVkIGZyb20gb3VyIGRhdGEgYXQgdGhlIHByb2plY3QgcmVwb3NpdG9yeSBvbiBHaXRodWIuIE90aGVyIGZpbGVzIGNhbiBiZSBkb3dubG9hZGVkIGZyb20gdGhlaXIgcmVzcGVjdGl2ZSBzb3VyY2VzLgoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgdGlkeSA9ICJzdHlsZXIiLCBmaWcuYXNwID0gMC42CikKCmxpYnJhcnkocGFjbWFuKQpwX2xvYWQoCiAgRW5oYW5jZWRWb2xjYW5vLCByZWFkeGwsIHRpZHl2ZXJzZSwgbGludHIsIEhvbW8uc2FwaWVucywgRE9TRSwgZGF0YS50YWJsZSwKICBjbHVzdGVyUHJvZmlsZXIsIGVucmljaHBsb3QsIGdndmVubiwgT3JnYW5pc21EYmksIGV1bGVyciwgaHR0cGdkLCB2aXJpZGlzLCBzY2FsZXMKKQoKdGhlbWVfcGlwZWxpbmUgPC0gdGhlbWVfc2V0KHRoZW1lX2dyYXkoKSArIHRoZW1lKAogIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsCiAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpKQpgYGAKCiMgUHJvdGVvbWUgZGF0YSBwcm9jZXNzaW5nCgpUaGlzIHNlY3Rpb24gaXMgZGVzaWduZWQgdG8gd29yayB3aXRoIG5vcm1hbGl6ZWQgYWJ1bmRhbmNlIGRhdGEgZnJvbSBQcm90ZW9tZSBEaXNjb3ZlcmVyLiBUaGUgYHRvdGFsX3BhdGhgIHZhcmlhYmxlIHNob3VsZCBwb2ludCBkaXJlY3RseSB0byB0aGUgRXhjZWwgZmlsZSBjb250YWluaW5nIHRoZSBkYXRhIHlvdSB3b3VsZCBsaWtlIHRvIHVzZS4gVGhlIHJhbmdlIGlkZW50aWZpZWQgaW4gdGhlIGByZWFkX3hsc3hgIGNvbW1hbmQgc2hvdWxkIGNvbnRhaW4gY29sdW1ucyBmcm9tICJBY2Nlc3Npb24iIHRvIHRoZSBmaW5hbCAiRm91bmQgaW4gU2FtcGxlOiIgY29sdW1uLgoKYGBge3IgdG90YWwtZGF0YS1pbXBvcnR9CiMgUmVhZCBpbiBub3JtYWxpemVkIGFidW5kYW5jZSBkYXRhIHN0YXJ0aW5nIHdpdGggQWNjZXNzaW9uIGNvbHVtbgp0b3RhbF9wYXRoIDwtICJ+L0RvY3VtZW50cy9SLzMzMDYwX0NvbnRyb2xfdnNfSW5mZWN0aW9uX0pNSV9hYnVuZGFuY2VzX25vcm1hbGl6ZWRhYnVuZGFuY2VzLnhsc3giCnRvdGFsX0tCIDwtIHJlYWRfeGxzeChwYXRoID0gdG90YWxfcGF0aCwgcmFuZ2UgPSBjZWxsX2NvbHMoYygiRDpCSSIpKSkKCnRvdGFsX0tCIDwtIHNlbGVjdCgKICB0b3RhbF9LQiwKICBjKCJBY2Nlc3Npb24iLAogICAgc3RhcnRzX3dpdGgoCiAgICAgIGMoIkFidW5kYW5jZSBSYXRpbyAobG9nMik6IiwgIkFidW5kYW5jZSBSYXRpbyBBZGouIFAtVmFsdWU6IiwgIkZvdW5kIGluIFNhbXBsZToiKQogICAgICApCiAgICApCiAgKQpgYGAKYGBge3IgdG90YWwta2V5LW1hcCwgbWVzc2FnZT1GQUxTRX0KIyBSZXRyaWV2ZSBrZXlzIGFuZCBhc3NpZ24gc3ltYm9sIHRvIG51bWVyaWNhbCBFbnRyZXogSUQKIyAoS0VHRyBtYXBwaW5nIGdpdmVzIGFuIGVycm9yIGlmIHRoaXMgaXNuJ3QgaW5jbHVkZWQpCiMgRXhhbXBsZTogZ2l2ZW4gRW50cmV6IElEIDMwNDMsIGFzc2lnbiBVbmlwcm90IGFjY2Vzc2lvbiBudW1iZXIgUDY4ODcxLAojIHRoZW4gbWFwIHRoZSBnZW5lIHN5bWJvbCBIQkIgZm9yIGVhc2llciBpbnRlcnByZXRhdGlvbiBsYXRlci4KcmVmbGlzdCA8LSBPcmdhbmlzbURiaTo6c2VsZWN0KAogIEhvbW8uc2FwaWVucywKICBrZXlzID0ga2V5cyhIb21vLnNhcGllbnMsa2V5dHlwZSA9ICJVTklQUk9UIiksCiAgY29sdW1ucyA9IGMoIkVOVFJFWklEIiwgIlNZTUJPTCIpLCBrZXl0eXBlID0gIlVOSVBST1QiKQp0b3RhbF9LQiA8LSBmdWxsX2pvaW4odG90YWxfS0IsIHJlZmxpc3QsIGJ5ID0gam9pbl9ieShBY2Nlc3Npb24gPT0gVU5JUFJPVCkpCgojIEdldHRpbmcgcmlkIG9mIHRoaXMgaHVnZSB2YXJpYWJsZSB0byBzYXZlIHNwYWNlCnJtKHJlZmxpc3QpCgojIERyb3AgaW52YWxpZCBkYXRhIChOQSksIGRyb3AgYW55IGR1cGxpY2F0ZSBjYXNlcywgcmVuYW1lIGNvbHVtbnMgZm9yIGNsYXJpdHkKdG90YWxfS0IgPC0gdG90YWxfS0IgJT4lCiAgZHJvcF9uYShTWU1CT0wpICU+JQogIGRpc3RpbmN0KFNZTUJPTCwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgcmVuYW1lKCJsb2cyKEZDKSIgPSBzdGFydHNfd2l0aCgiQWJ1bmRhbmNlIFJhdGlvIChsb2cyKSIpLAogICAgICAgICAiUCB2YWx1ZSIgPSBzdGFydHNfd2l0aCgiQWJ1bmRhbmNlIFJhdGlvIEFkai4gUC1WYWx1ZSIpCikKCiMgUmVtb3ZlIHBvaW50cyB3aXRoIGludmFsaWQgUCB2YWx1ZXMgKGhvcml6b250YWwgbGluZSBhdCB0aGUgdG9wIG9mIGEgcGxvdCksCiMgYXNzaWduIHN5bWJvbHMgdG8gcm93IG5hbWVzCnRvdGFsX0tCIDwtIHRvdGFsX0tCICU+JQogIGZpbHRlcihgUCB2YWx1ZWAgPiA1LjNlLTE2KSAlPiUKICBtdXRhdGUoIlNZTUJPTDIiID0gYFNZTUJPTGApICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gIlNZTUJPTCIpCgojIFNldCB0aHJlc2hvbGQgdmFsdWVzIGZvciBwcm90ZW9tZSBhbmFseXNpcwp0b3RhbF9wX3RocmVzaG9sZCA8LSAwLjA1CnRvdGFsX0ZDX3RocmVzaG9sZCA8LSAwLjUKCiMgU3Vic2V0IHRoZSBkYXRhIGZvciBwbG90dGluZyBsYXRlciAtIGFueXRoaW5nIHdpdGggUCA8IHRocmVzaG9sZAp0b3RhbF9zaWduaWZpY2FudCA8LSBmaWx0ZXIodG90YWxfS0IsIGBQIHZhbHVlYCA8IHRvdGFsX3BfdGhyZXNob2xkKQpgYGAKCiMjIFZvbGNhbm8KCkZpZ3VyZSAxLjFhIGlzIGEgZ2VuZXJhbCB2b2xjYW5vIHBsb3QsIHNob3dpbmcgYSB2aXN1YWxpemF0aW9uIG9mIHRoZSBmdWxsIGRhdGFzZXQuCgpUaGUgYG1heC5vdmVybGFwc2AgcGFyYW1ldGVyIGluIHRoZSBgZ2VvbV90ZXh0X3JlcGVsYCBjYWxsIHNob3VsZCBiZSBjYXJlZnVsbHkgYWRqdXN0ZWQuIFRoZSBwbG90IG1heSBoYXZlIHRvIGJlIGdlbmVyYXRlZCBtdWx0aXBsZSB0aW1lcyB0byBnZXQgYW4gYXBwcm9wcmlhdGUgbGFiZWxpbmcgY291bnQuIFNldHRpbmcgdGhpcyB2YWx1ZSB0byBgSW5mYCBpcyBwb3NzaWJsZSwgYnV0IHdpbGwgbGlrZWx5IG92ZXJ3aGVsbSB0aGUgcGxvdCBhbmQgbm8gcG9pbnRzIHdpbGwgYmUgdmlzaWJsZS4KCmBgYHtyIHRvdGFsLXZvbGNhbm8tcGxvdH0KIyBHZW5lcmF0aW5nIG92ZXJhbGwgdm9sY2FubyBwbG90CiMgU2V0dGluZyBjYXRlZ29yaWVzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbjoKIyBub3Qgc2lnbmlmaWNhbnQsIFAtdmFsdWUgb25seSwgRkMgb25seSwgYm90aAp0b3RhbF9LQiREaWZmRXhwIDwtIGNhc2Vfd2hlbigKICBhYnModG90YWxfS0IkYGxvZzIoRkMpYCkgPj0gdG90YWxfRkNfdGhyZXNob2xkICYKICAgIHRvdGFsX0tCJGBQIHZhbHVlYCA+IHRvdGFsX3BfdGhyZXNob2xkIH4gIkZDIG9ubHkiLAogIGFicyh0b3RhbF9LQiRgbG9nMihGQylgKSA8IHRvdGFsX0ZDX3RocmVzaG9sZCAmCiAgICB0b3RhbF9LQiRgUCB2YWx1ZWAgPD0gdG90YWxfcF90aHJlc2hvbGQgfiAiUCB2YWwuIG9ubHkiLAogIGFicyh0b3RhbF9LQiRgbG9nMihGQylgKSA+PSB0b3RhbF9GQ190aHJlc2hvbGQgJgogICAgdG90YWxfS0IkYFAgdmFsdWVgIDw9IHRvdGFsX3BfdGhyZXNob2xkIH4gIkZDIGFuZCBQIHZhbC4iLAogIC5kZWZhdWx0ID0gIk5TIgopCnRvdGFsX0tCJERpZmZFeHAgPC0gZmFjdG9yKHRvdGFsX0tCJERpZmZFeHAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkZDIGFuZCBQIHZhbC4iLCAiUCB2YWwuIG9ubHkiLCAiRkMgb25seSIsICJOUyIpKQoKIyBTZXR0aW5nIGFscGhhIHZhbHVlcyBhbmQgbGFiZWxzIHNvIHRoZSBzaWduaWZpY2FudCBwb2ludHMgYXJlIG1vc3QgdmlzaWJsZQp0b3RhbF9LQiRERUFscGhhIDwtIDAuMwp0b3RhbF9LQiRERUFscGhhW2Ficyh0b3RhbF9LQiRgbG9nMihGQylgKSA+PSB0b3RhbF9GQ190aHJlc2hvbGQgJgogICAgICAgICAgICAgICAgICAgdG90YWxfS0IkYFAgdmFsdWVgIDw9IHRvdGFsX3BfdGhyZXNob2xkXSA8LSAwLjgKCnRvdGFsX0tCJGxhYmVscyA8LSBpZl9lbHNlKAogIGFicyh0b3RhbF9LQiRgbG9nMihGQylgKSA+PSB0b3RhbF9GQ190aHJlc2hvbGQgJgogICAgdG90YWxfS0IkYFAgdmFsdWVgIDw9IHRvdGFsX3BfdGhyZXNob2xkLCB0b3RhbF9LQiRTWU1CT0wyLCAiIikKCmdncGxvdCh0b3RhbF9LQiwgYWVzKHggPSBgbG9nMihGQylgLCB5ID0gLWxvZzEwKGBQIHZhbHVlYCkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBEaWZmRXhwLCBhbHBoYSA9IERFQWxwaGEpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyh0b3RhbF9GQ190aHJlc2hvbGQsIC10b3RhbF9GQ190aHJlc2hvbGQpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAodG90YWxfcF90aHJlc2hvbGQpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbGFiZWxzKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgc2l6ZSA9IHJlbCgzKSkgKwogIGd1aWRlcyhhbHBoYSA9ICJub25lIikgKwogIGxhYnMoeCA9ICJsb2cyKEZvbGQgQ2hhbmdlKSIsIHkgPSAiLWxvZzEwKFAgdmFsdWUpIiwKICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxLjFhOiBWb2xjYW5vIHBsb3QiLAogICAgICAgY29sb3IgPSAiQ2F0ZWdvcnkiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgKICAgIGFlc3RoZXRpY3MgPSAiY29sb3IiLCBvcHRpb24gPSAibWFnbWEiLAogICAgYmVnaW4gPSAwLjIsIGVuZCA9IDAuOQogICkKYGBgCgpJZiB0aGVyZSBhcmUga25vd24gZ2VuZXMgb2YgaW50ZXJlc3QgdG8gYmUgaW5jbHVkZWQgaW4gYSBwbG90LCBGaWd1cmUgMS4xYiBwbG90cyB0aGVzZSBwb2ludHMgb24gdG9wIG9mIHRoZSBmdWxsIGRhdGFzZXQgYXMgYSBiYWNrZ3JvdW5kLgoKYGBge3IgdG90YWwtZ3JvdXBpbmd9CiMgRGVmaW5pbmcga25vd24gZ2VuZXMgb2YgaW50ZXJlc3QgZm9yIGEgc2Vjb25kIHBsb3QKR3JvdXAxIDwtIGMoIk1PUkY0TDEiLCAiU0FNU04xIiwgIkRUWDNMIiwgIk1CRDMiLCAgIkhTRjEiLCAiRVlBMyIsCiAgICAgICAgICAgICAgICJOQ0FQRDIiLCAiVEFGOSIsICJQUFA0QyIsICJaTkY2MzgiKQpHcm91cDIgPC0gYygiQ0QyNzYiLCAiQ0RLNiIsICJDRDkiLCAiQ0Q4MiIsICJORktCMSIsICJCU1QyIiwgIkNUU0giKQoKIyBBc3NpZ25pbmcgc2hhcGVzIGFuZCBsYWJlbHMgZm9yIGVhY2ggZ3JvdXAgaW4gdGhlIHZvbGNhbm8gcGxvdCwKIyBsZWF2aW5nIHBvaW50cyB1bmxhYmVsZWQgaWYgb3V0c2lkZSB0aGUgZ3JvdXBzCnRvdGFsX0tCJENhdFNoYXBlcyA8LSBjYXNlX3doZW4oCiAgdG90YWxfS0IkU1lNQk9MMiAlaW4lIEdyb3VwMSB+ICIxIiwKICB0b3RhbF9LQiRTWU1CT0wyICVpbiUgR3JvdXAyIH4gIjIiLAogIC5kZWZhdWx0ID0gIjIwIgopCgp0b3RhbF9LQiRDYXROYW1lcyA8LSBjYXNlX3doZW4oCiAgdG90YWxfS0IkU1lNQk9MMiAlaW4lIEdyb3VwMSB8IHRvdGFsX0tCJFNZTUJPTDIgJWluJSBHcm91cDIgfiB0b3RhbF9LQiRTWU1CT0wyLAogIC5kZWZhdWx0ID0gIiIKKQpgYGAKCmBgYHtyIHRvdGFsLWdyb3VwZWQtdm9sY2Fuby1wbG90fQojIEdlbmVyYXRpbmcgdm9sY2FubyBwbG90CiMgYXJyYW5nZSgpIGNvbW1hbmQgc29ydHMgdGhlIG5hbWVkIHBvaW50cyBvbiB0b3Agc28gdGhleSBhcmVuJ3QgYnVyaWVkIHVuZGVybmVhdGgKZ2dwbG90KGFycmFuZ2UodG90YWxfS0IsIENhdE5hbWVzKSwgYWVzKHggPSBgbG9nMihGQylgLCB5ID0gLWxvZzEwKGBQIHZhbHVlYCkpKSArCiAgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBDYXRTaGFwZXMsIGNvbG9yID0gQ2F0U2hhcGVzKSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGModG90YWxfRkNfdGhyZXNob2xkLCAtdG90YWxfRkNfdGhyZXNob2xkKSwgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAodG90YWxfcF90aHJlc2hvbGQpLCBhbHBoYSA9IDAuNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBDYXROYW1lcyksIHNpemUgPSByZWwoMyksIHNob3cubGVnZW5kID0gRkFMU0UsIG1heC5vdmVybGFwcyA9IEluZikgKwogIGd1aWRlcyhhbHBoYSA9ICJub25lIiwgc2hhcGUgPSAibm9uZSIpICsKICBsYWJzKHggPSAibG9nMihGb2xkIENoYW5nZSkiLCB5ID0gIi1sb2cxMChQIHZhbHVlKSIsCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMS4xYjogVm9sY2FubyBwbG90IHdpdGggZ3JvdXBzIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoIkNhdGVnb3J5IiwKICAgIGFlc3RoZXRpY3MgPSAiY29sb3IiLCBvcHRpb24gPSAibWFnbWEiLAogICAgYmVnaW4gPSAwLjIsIGVuZCA9IDAuOSwKICAgIGxhYmVscyA9IGMoIkdyb3VwIDIiLCAiR3JvdXAgMSIsICJOb25lIikKICApCmBgYAoKIyMgUGF0aHdheSBlbnJpY2htZW50CgpUaGlzIHNlY3Rpb24gcGVyZm9ybXMgYSBwYXRod2F5IGFuYWx5c2lzIG9uIHRoZSBzaWduaWZpY2FudCBwcm90ZWlucyBhbG9uZSB0byByZWR1Y2UgY29tcHV0YXRpb25hbCBsb2FkIHdoZW4gY2FsbGluZyBgZW5yaWNoR09gIGFuZCBgZW5yaWNoS0VHR2AuIFRoZXNlIHZhcmlhYmxlcyBjYW4gYmUgZWFzaWx5IGFkanVzdGVkIHRvIGFuYWx5emUgYWxsIHByb3RlaW5zIHRvZ2V0aGVyIGlmIGRlc2lyZWQuCgpJbiBvdXIgc291cmNlIGRhdGFzZXQsIHRoZXJlIHdlcmUgbm8gZG93bnJlZ3VsYXRlZCBLRUdHIHBhdGh3YXlzLiBUaGlzIGNhdXNlcyBlcnJvciBtZXNzYWdlcywgc28gdGhlICJUb3RhbCBLRUdHIGRvd24iIHBsb3QgaGFzIGJlZW4gcmVtb3ZlZC4gVGhlIGNhbGwgdG8gYGxhYmVsc2AgYmVsb3cgaWxsdXN0cmF0ZXMgYSBkaWZmaWN1bHR5IHdpdGggcGxvdHRpbmcgbG9uZyBvbnRvbG9neSB0ZXJtcywgYnV0IHRoZSBgbGFiZWxfZm9ybWF0YCBwYXJhbWV0ZXIgaXMgYW4gZWFzeSB3YXkgdG8gc29sdmUgdGhpcy4gT3V0c2lkZSBvZiBgZW5yaWNocGxvdDo6ZG90cGxvdGAsIGNhbGxpbmcgYGxhYmVsX3dyYXBgIGZyb20gYGxhYmVsc2AgYW5kIHBhc3NpbmcgYSBjaGFyYWN0ZXIgY291bnQgdG8gd3JhcCBhdCBpcyBhIGdvb2Qgc29sdXRpb24uIFdlIGFsc28gc2hvdyBhIHdheSB0byBtYW51YWxseSByZXBsYWNlIGxvbmcgbmFtZXMgbGF0ZXIgaW4gdGhpcyBzZWN0aW9uLgoKYGBge3IgdG90YWwtZW5yaWNofQojIFNlcGFyYXRlIHRvdGFsX3NpZ25pZmljYW50IHN1YnNldCBpbnRvIHVwcmVndWxhdGVkIHZzIGRvd25yZWd1bGF0ZWQKdG90YWxfc2lnbmlmaWNhbnRfdXAgPC0gZmlsdGVyKHRvdGFsX3NpZ25pZmljYW50LCBgbG9nMihGQylgID4gMCkKdG90YWxfc2lnbmlmaWNhbnRfZG93biA8LSBmaWx0ZXIodG90YWxfc2lnbmlmaWNhbnQsIGBsb2cyKEZDKWAgPCAwKQoKIyBFbnJpY2hpbmcgZm9yICp0b3RhbCogR08gdGVybXMgLSBvcHRpb25hbCwgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXplIG92ZXJhbGwgcmVzdWx0cwp0b3RhbF9HT191cCA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF91cCRFTlRSRVpJRCwgIm9yZy5Icy5lZy5kYiIpCnRvdGFsX0dPX2Rvd24gPC0gZW5yaWNoR08odG90YWxfc2lnbmlmaWNhbnRfZG93biRFTlRSRVpJRCwgIm9yZy5Icy5lZy5kYiIpCgojIEVucmljaCBmb3IgS0VHRyBwYXRod2F5cwp0b3RhbF9LRUdHX3VwIDwtIGVucmljaEtFR0codG90YWxfc2lnbmlmaWNhbnRfdXAkRU5UUkVaSUQpCnRvdGFsX0tFR0dfZG93biA8LSBlbnJpY2hLRUdHKHRvdGFsX3NpZ25pZmljYW50X2Rvd24kRU5UUkVaSUQpCgojIGRvdHBsb3QoKSBjYW4gYmUgdXNlZCBmb3IgYW4gb3ZlcnZpZXcgb2YgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBrZXkgdGVybXMKZG90cGxvdCh0b3RhbF9LRUdHX3VwLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxLjJhOiBUb3RhbCBLRUdHIHVwIikgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpkb3RwbG90KHRvdGFsX0dPX3VwLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxLjJiOiBUb3RhbCBHTyB1cCAodW53cmFwcGVkKSIpICsKICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKZG90cGxvdCh0b3RhbF9HT191cCwgc2hvd0NhdGVnb3J5ID0gMjAsIGZvbnQuc2l6ZSA9IHJlbCgxKSwKICAgICAgICB0aXRsZSA9ICJGaWd1cmUgMS4yYzogVG90YWwgR08gdXAgKHdyYXBwZWQpIiwgbGFiZWxfZm9ybWF0ID0gNjApICsKICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKZG90cGxvdCh0b3RhbF9HT19kb3duLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxLjJkOiBUb3RhbCBHTyBkb3duIikgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCkZvciBlYWNoIHN1YnNldCBvZiBkYXRhLCB0aGUgdG9wIDEwIHRlcm1zIGFyZSBmb3VuZCBpbiBlYWNoIEdPIGFzcGVjdCAobW9sZWN1bGFyIGZ1bmN0aW9uIChNRiksIGJpb2xvZ2ljYWwgcHJvY2VzcyAoQlApLCBjZWxsdWxhciBjb21wb25lbnQgKENDKSkuIFRvIGluY2x1ZGUgbW9yZSB0ZXJtcywgYWRqdXN0IHRoZSBgbiA9IDEwYCBwYXJhbWV0ZXIgaW4gdGhlIGNhbGxzIHRvIGBzbGljZV9taW4oKWAuCgpJbiB0aGlzIHNlY3Rpb24sIHdlIGFsc28gcmV3b3JkIHNvbWUgdmVyeSBsb25nIEdPIHRlcm1zIHRvIGZpdCB0aGVtIGluIGFuIGFwcHJvcHJpYXRlLXNpemUgcGxvdC4gVGhpcyBsaXN0IHdpbGwgbmVlZCB0byBiZSBhZGp1c3RlZCBiYXNlZCBvbiB0aGUgZW5yaWNoZWQgdGVybXMgaW4gYW4gaW5kaXZpZHVhbCBkYXRhc2V0LgoKYGBge3IgdG90YWwtR08tdXAtc3BsaXR9CiMgU2VwYXJhdGluZyB0aGUgR08gdGVybXMgYnkgb250b2xvZ3kgYXNwZWN0CnRvdGFsX0dPX3VwX01GIDwtIGVucmljaEdPKHRvdGFsX3NpZ25pZmljYW50X3VwJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLCBvbnQgPSAiTUYiCiAgKQp0b3RhbF9HT191cF9CUCA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF91cCRFTlRSRVpJRCwKICAib3JnLkhzLmVnLmRiIiwgb250ID0gIkJQIgogICkKdG90YWxfR09fdXBfQ0MgPC0gZW5yaWNoR08odG90YWxfc2lnbmlmaWNhbnRfdXAkRU5UUkVaSUQsCiAgIm9yZy5Icy5lZy5kYiIsIG9udCA9ICJDQyIKICApCmBgYAoKYGBge3IgdG90YWwtR08tdXAtY2xlYW59CiMgVGFraW5nIHRoZSB0b3AgMTAgZW5yaWNoZWQgR08gdGVybXMgaW4gZWFjaCBjYXRlZ29yeSBieSBQIHZhbHVlCnRvdGFsX0dPX3VwX3NsaWNlX01GIDwtIHNsaWNlX21pbigKICBmaWx0ZXIodG90YWxfR09fdXBfTUZAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKdG90YWxfR09fdXBfc2xpY2VfQlAgPC0gc2xpY2VfbWluKAogIGZpbHRlcih0b3RhbF9HT191cF9CUEByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQp0b3RhbF9HT191cF9zbGljZV9DQyA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHRvdGFsX0dPX3VwX0NDQHJlc3VsdCwgcC5hZGp1c3QgPCAwLjA1KSwKICBvcmRlcl9ieSA9IGBwLmFkanVzdGAsIG4gPSAxMAopCiMgQ29tYmluaW5nIHRvcCAxMCB0ZXJtcyBpbiBlYWNoIGFzcGVjdAp0b3RhbF9HT191cF9zbGljZV9BTEwgPC0gYmluZF9yb3dzKAogICJNRiIgPSB0b3RhbF9HT191cF9zbGljZV9NRiwKICAiQlAiID0gdG90YWxfR09fdXBfc2xpY2VfQlAsCiAgIkNDIiA9IHRvdGFsX0dPX3VwX3NsaWNlX0NDLAogIC5pZCA9ICJPbnRvbG9neSIKKQp0b3RhbF9HT191cF9zbGljZV9BTEwkR1IgPC0gcGFyc2VfcmF0aW8odG90YWxfR09fdXBfc2xpY2VfQUxMJEdlbmVSYXRpbykKCiMgQWRkZWQgaW4gdG8gc2hvcnRlbiBuYW1lcyB3aGVuIHBsb3R0ZWQgLSB0aGlzIGNhbiBiZSByZW1vdmVkIG9yIGFkanVzdGVkCnRvdGFsX0dPX3VwX3NsaWNlX0FMTFsiRGVzY3JpcHRpb24iXVt0b3RhbF9HT191cF9zbGljZV9BTExbIkRlc2NyaXB0aW9uIl0gPT0gImh5ZHJvbGFzZSBhY3Rpdml0eSwgYWN0aW5nIG9uIGNhcmJvbi1uaXRyb2dlbiAoYnV0IG5vdCBwZXB0aWRlKSBib25kcyJdIDwtICJoeWRyb2xhc2UsIEMtTiBub24tcGVwdGlkZSBib25kcyIKdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdW3RvdGFsX0dPX3VwX3NsaWNlX0FMTFsiRGVzY3JpcHRpb24iXSA9PSAiaHlkcm9sYXNlIGFjdGl2aXR5LCBhY3Rpbmcgb24gY2FyYm9uLW5pdHJvZ2VuIChidXQgbm90IHBlcHRpZGUpIGJvbmRzLCBpbiBjeWNsaWMgYW1pZGluZXMiXSA8LSAiaHlkcm9sYXNlLCBDLU4gbm9uLXBlcHRpZGUgYm9uZHMgaW4gY3ljbGljIGFtaWRpbmVzIgp0b3RhbF9HT191cF9zbGljZV9BTExbIkRlc2NyaXB0aW9uIl1bdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdID09ICJhbnRpZ2VuIHByb2Nlc3NpbmcgYW5kIHByZXNlbnRhdGlvbiBvZiBlbmRvZ2Vub3VzIHBlcHRpZGUgYW50aWdlbiB2aWEgTUhDIGNsYXNzIEkiXSA8LSAiZW5kb2dlbm91cyBhbnRpZ2VuIHByb2Nlc3NpbmcgYW5kIHByZXNlbnRhdGlvbiAoTUhDIGNsYXNzIEkpIgp0b3RhbF9HT191cF9zbGljZV9BTExbIkRlc2NyaXB0aW9uIl1bdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdID09ICJhbnRpZ2VuIHByb2Nlc3NpbmcgYW5kIHByZXNlbnRhdGlvbiBvZiBwZXB0aWRlIGFudGlnZW4gdmlhIE1IQyBjbGFzcyBJIl0gPC0gInBlcHRpZGUgYW50aWdlbiBwcm9jZXNzaW5nIGFuZCBwcmVzZW50YXRpb24gKE1IQyBjbGFzcyBJKSIKdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdW3RvdGFsX0dPX3VwX3NsaWNlX0FMTFsiRGVzY3JpcHRpb24iXSA9PSAiYW50aWdlbiBwcm9jZXNzaW5nIGFuZCBwcmVzZW50YXRpb24gb2YgZW5kb2dlbm91cyBwZXB0aWRlIGFudGlnZW4iXSA8LSAiZW5kb2dlbm91cyBhbnRpZ2VuIHByb2Nlc3NpbmcgYW5kIHByZXNlbnRhdGlvbiIKdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdW3RvdGFsX0dPX3VwX3NsaWNlX0FMTFsiRGVzY3JpcHRpb24iXSA9PSAiZXhvbnVjbGVhc2UgYWN0aXZpdHksIGFjdGl2ZSB3aXRoIGVpdGhlciByaWJvLSBvciBkZW94eXJpYm9udWNsZWljIGFjaWRzIGFuZCBwcm9kdWNpbmcgNSctcGhvc3Bob21vbm9lc3RlcnMiXSA8LSAiNSctZXhvbnVjbGVhc2VzIgoKCiMgUmVvcmRlcmluZyB0aGUgdG9wIHRlcm1zIGJ5IGdlbmUgcmF0aW8KdG90YWxfR09fdXBfc2xpY2VfQUxMJERlc2NyaXB0aW9uIDwtIGZjdF9yZW9yZGVyKAogIHRvdGFsX0dPX3VwX3NsaWNlX0FMTCREZXNjcmlwdGlvbiwgdG90YWxfR09fdXBfc2xpY2VfQUxMJEdSCikKYGBgCgpgYGB7ciB0b3RhbC1LRUdHLXVwLWVucmljaH0KIyBSZXBlYXQgZm9yIEtFR0cKIyBUYWtpbmcgdGhlIHRvcCAxMCBlbnJpY2hlZCB0ZXJtcyBieSBQIHZhbHVlCnRvdGFsX0tFR0dfdXBfc2xpY2UgPC0gc2xpY2VfbWluKGZpbHRlcih0b3RhbF9LRUdHX3VwQHJlc3VsdCwgcC5hZGp1c3QgPCAwLjA1KSwKICBvcmRlcl9ieSA9IGBwLmFkanVzdGAsIG4gPSAxMAopCiMgUmVvcmRlcmluZyB0aGUgdG9wIHRlcm1zIGJ5IGdlbmUgcmF0aW8KdG90YWxfS0VHR191cF9zbGljZSRHUiA8LSBwYXJzZV9yYXRpbyh0b3RhbF9LRUdHX3VwX3NsaWNlJEdlbmVSYXRpbykKdG90YWxfS0VHR191cF9zbGljZSREZXNjcmlwdGlvbiA8LSBmY3RfcmVvcmRlcigKICB0b3RhbF9LRUdHX3VwX3NsaWNlJERlc2NyaXB0aW9uLCB0b3RhbF9LRUdHX3VwX3NsaWNlJEdSCikKdG90YWxfS0VHR191cF9zbGljZSRPbnRvbG9neSA8LSAiS0VHRyIKYGBgCgpgYGB7ciB0b3RhbC11cC1jb21iaW5lfQojIENvbWJpbmluZyBHTyBhbmQgS0VHRyBmb3IgdXByZWd1bGF0ZWQgcHJvdGVpbnMKdG90YWxfdXBfYm91bmQgPC0gYmluZF9yb3dzKAogIHRvdGFsX0dPX3VwX3NsaWNlX0FMTCwgdG90YWxfS0VHR191cF9zbGljZQopCnRvdGFsX3VwX2JvdW5kJERlc2NyaXB0aW9uIDwtIGZjdF9yZW9yZGVyKAogIHRvdGFsX3VwX2JvdW5kJERlc2NyaXB0aW9uLCB0b3RhbF91cF9ib3VuZCRHUgopCgojIE1hbnVhbGx5IG9yZGVyaW5nIGZhY3RvcnMgdG8gZGlzcGxheSBhbGwgR08gYXNwZWN0cyB0b2dldGhlcgp0b3RhbF91cF9ib3VuZCRPbnRvbG9neSA8LSBmYWN0b3IodG90YWxfdXBfYm91bmQkT250b2xvZ3ksCiAgbGV2ZWxzID0gYygiQlAiLCAiQ0MiLCAiTUYiLCAiS0VHRyIpCikKYGBgCgpgYGB7ciB0b3RhbC11cC1wYXRod2F5cywgZmlnLmFzcCA9IDAuOX0KIyBHcmFwaGluZyBhbGwgNCBjYXRlZ29yaWVzIHRvZ2V0aGVyCmdncGxvdCh0b3RhbF91cF9ib3VuZCwKICAjIEdlbmUgcmF0aW8gb24gWCBheGlzLCB2ZXJib3NlIHBhdGh3YXkgZGVzY3JpcHRpb24gb24gWSBheGlzCiAgIyBDb2xvcmluZyBieSBhZGp1c3RlZCBQIHZhbHVlIGFuZCBzaXplIGJ5IHRoZSBjb3VudCBvZiBnZW5lcyBpbiBhIHBhdGh3YXkKICBhZXMoeCA9IEdSLCB5ID0gRGVzY3JpcHRpb24sIGNvbG9yID0gcC5hZGp1c3QsIHNpemUgPSBDb3VudCkKICApICsKICBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscyA9IGxhYmVsX3dyYXAoNzApKSArCiAgZ2VvbV9jb3VudCgpICsKICBsYWJzKAogICAgeCA9ICJHZW5lIHJhdGlvIiwgeSA9ICJQYXRod2F5IiwKICAgIHRpdGxlID0gIkZpZ3VyZSAxLjJlOiBVcHJlZ3VsYXRlZCBwYXRod2F5cyIKICApICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIG5hbWUgPSAiQWRqdXN0ZWQgUCIsIGJlZ2luID0gMCwgZW5kID0gMC45CiAgKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjUsIDI0KSkgKwogIGZhY2V0X2dyaWQoT250b2xvZ3kgfiAuLAogICAgZHJvcCA9IFRSVUUsIHNwYWNlID0gImZyZWVfeSIsIHNjYWxlcyA9ICJmcmVlX3kiCiAgKSArCiAgdGhlbWUoCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjYsICJsaW5lcyIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgxLjEpKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAibGlnaHQgZ3JheSIpLAogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5OTAiKQogICkKYGBgCgpgYGB7ciB0b3RhbC1HTy1kb3duLXNwbGl0fQojIFNhbWUgcHJvY2VzcyBhcyB0aGUgcHJldmlvdXMgY2h1bmsKIyBTZXBhcmF0aW5nIHRoZSBHTyB0ZXJtcyBieSBvbnRvbG9neSBhc3BlY3QKdG90YWxfR09fZG93bl9NRiA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLCBvbnQgPSAiTUYiCikKdG90YWxfR09fZG93bl9CUCA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLCBvbnQgPSAiQlAiCikKdG90YWxfR09fZG93bl9DQyA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLCBvbnQgPSAiQ0MiCikKYGBgCgpgYGB7ciB0b3RhbC1kb3duLWNsZWFufQojIFRha2luZyB0aGUgdG9wIDEwIGVucmljaGVkIHRlcm1zIGluIGVhY2ggY2F0ZWdvcnkgYnkgUCB2YWx1ZQp0b3RhbF9HT19kb3duX3NsaWNlX01GIDwtIHNsaWNlX21pbigKICBmaWx0ZXIodG90YWxfR09fZG93bl9NRkByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQp0b3RhbF9HT19kb3duX3NsaWNlX0JQIDwtIHNsaWNlX21pbigKICBmaWx0ZXIodG90YWxfR09fZG93bl9CUEByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQp0b3RhbF9HT19kb3duX3NsaWNlX0NDIDwtIHNsaWNlX21pbigKICBmaWx0ZXIodG90YWxfR09fZG93bl9DQ0ByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQoKIyBDb21iaW5pbmcgdG9wIDEwIHRlcm1zIGluIGVhY2ggYXNwZWN0CnRvdGFsX2Rvd25fYm91bmQgPC0gYmluZF9yb3dzKAogICJNRiIgPSB0b3RhbF9HT19kb3duX3NsaWNlX01GLAogICJCUCIgPSB0b3RhbF9HT19kb3duX3NsaWNlX0JQLAogICJDQyIgPSB0b3RhbF9HT19kb3duX3NsaWNlX0NDLAogIC5pZCA9ICJPbnRvbG9neSIKKQp0b3RhbF9kb3duX2JvdW5kJEdSIDwtIHBhcnNlX3JhdGlvKHRvdGFsX2Rvd25fYm91bmQkR2VuZVJhdGlvKQoKIyBSZW9yZGVyaW5nIHRoZSB0b3AgdGVybXMgYnkgZ2VuZSByYXRpbwp0b3RhbF9kb3duX2JvdW5kJERlc2NyaXB0aW9uIDwtIGZjdF9yZW9yZGVyKAogIHRvdGFsX2Rvd25fYm91bmQkRGVzY3JpcHRpb24sIHRvdGFsX2Rvd25fYm91bmQkR1IKKQp0b3RhbF9kb3duX2JvdW5kJE9udG9sb2d5IDwtIGZhY3Rvcih0b3RhbF9kb3duX2JvdW5kJE9udG9sb2d5LAogIGxldmVscyA9IGMoIkJQIiwgIkNDIiwgIk1GIiwgIktFR0ciKQopCgojIEtFR0cgZW5yaWNobWVudCBkaWQgbm90IHNob3cgYW55dGhpbmcgc28gaXQgY2F1c2VkIGFuIGVycm9yIGluIG91ciBkYXRhIGFuZCBoYXMgYmVlbiByZW1vdmVkCiMgSWYgZGVzaXJlZCwgS0VHRyBlbnJpY2htZW50IGNhbiBiZSBwZXJmb3JtZWQgaGVyZSBhcyBpbiB0aGUgcHJldmlvdXMgY2h1bmsKYGBgCgpgYGB7ciB0b3RhbC1kb3duLXBhdGh3YXlzfQpnZ3Bsb3QodG90YWxfZG93bl9ib3VuZCwKICAgICAgIGFlcyh4ID0gR1IsIHkgPSBEZXNjcmlwdGlvbiwgY29sb3IgPSBwLmFkanVzdCwgc2l6ZSA9IENvdW50KSkgKwogIGdlb21fY291bnQoKSArCiAgbGFicygKICAgIHggPSAiR2VuZSByYXRpbyIsIHkgPSAiUGF0aHdheSIsCiAgICB0aXRsZSA9ICJGaWd1cmUgMS4yZjogRG93bnJlZ3VsYXRlZCBwYXRod2F5cyIKICApICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIG5hbWUgPSAiQWRqdXN0ZWQgUCIsIGJlZ2luID0gMCwgZW5kID0gMC45CiAgKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjUsIDI0KSkgKwogIGZhY2V0X2dyaWQoT250b2xvZ3kgfiAuLAogICAgZHJvcCA9IFRSVUUsIHNwYWNlID0gImZyZWVfeSIsCiAgICBzY2FsZXMgPSAiZnJlZV95IgogICkgKwogIHRoZW1lKAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMC42LCAibGluZXMiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMS4xKSksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gImxpZ2h0IGdyYXkiKSwKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTkwIikKICApCmBgYAoKRmlndXJlIDEuMmMgY2x1c3RlcnMgdGVybXMgYnkgZnVuY3Rpb24sIHdoaWNoIHNob3dzIGVucmljaGVkIGRvbWFpbnMuCgpgYGB7ciB0b3RhbC1tYXAsIGZpZy5hc3AgPSAxfQojIFBsb3R0aW5nIHRoZSBlbnJpY2htZW50IHBsb3QgZm9yIG9uZSBhc3BlY3Qgb2YgdXByZWd1bGF0ZWQgcGF0aHdheXMKIyBFbnJpY2ggZmlyc3Qgd2l0aCBwYWlyd2lzZV90ZXJtX3NpbSwgdGhlbiBnZW5lcmF0ZSB0aGUgcGxvdCBpdHNlbGYKdG90YWxfR09fdXBfTUYgPC0gcGFpcndpc2VfdGVybXNpbSh0b3RhbF9HT191cF9NRikKZW1hcHBsb3QodG90YWxfR09fdXBfTUYsIGxheW91dCA9ICJuaWNlbHkiLCBub2RlX2xhYmVsID0gIm5vbmUiKSArCiAgZ2d0aXRsZSgiRmlndXJlIDEuMmc6IFVwcmVndWxhdGVkIGVucmljaG1lbnQgcGxvdCAoTUYpIikgKwogIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBzdHJfd3JhcChuYW1lLCB3aWR0aCA9IDYwKSkpICsKICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCAxLCAxLCAxKSkKYGBgCgojIFBob3NwaG9wcm90ZW9tZSBkYXRhIHByb2Nlc3NpbmcKClRoaXMgc2VjdGlvbiBpcyB3cml0dGVuIHRvIGludGVycHJldCBwaG9zcGhvcHJvdGVvbWUgZGF0YS4gSGVyZSwgd2UgZG8gbm90IGFjY291bnQgZm9yIHBhcnRpY3VsYXIgcGhvc3Bob3J5bGF0aW9uIHNpdGVzIG9yIHRoZWlyIGZ1bmN0aW9ucy4gVGhlIG9yaWdpbmFsIGRhdGEgaXMgZnJvbSBAY2hvdWRoYXJ5MjAyMC4gQmVsb3cgaXMgYSByZXByb2Nlc3Npbmcgb2YgdGhpcyBkYXRhIHRvIGFsaWduIHdpdGggdGhlIHRvdGFsIGFuZCBoaXN0b25lIHByb3Rlb21lLgoKYGBge3IgcGhvc3Boby1pbXBvcnQsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIFJlYWQgaW4gcGhvc3Bob3Byb3Rlb21lIGRhdGEsIGFzc2lnbiBzeW1ib2wgdG8gcm93IG5hbWVzCiMgQWxyZWFkeSBmb3JtYXR0ZWQgc28gdGhlcmUncyBubyBuZWVkIHRvIG1hcCB3aXRoIHJlZmxpc3QKcGhvc3Bob19yYXcgPC0gcmVhZF94bHN4KCJ+L0RvY3VtZW50cy9SL1BYRF9yYXcueGxzeCIpCnBob3NwaG9fcmF3IDwtIHBob3NwaG9fcmF3ICU+JQogIG11dGF0ZSgiU1lNQk9MMiIgPSBgU1lNQk9MYCkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJTWU1CT0wiKSAlPiUKICBkcm9wX25hKFNZTUJPTDIsIGBsb2cyKEZDKWAsIGBQIHZhbHVlYCkgJT4lCiAgZGlzdGluY3QoU1lNQk9MMiwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgbXV0YXRlKGAuLi42YCA9IE5VTEwpCgojIFNldCB0aHJlc2hvbGQgdmFsdWVzIGZvciBwcm90ZW9tZSBhbmFseXNpcwpwaG9zcGhvX3BfdGhyZXNob2xkIDwtIDAuMDUKcGhvc3Bob19GQ190aHJlc2hvbGQgPC0gMC41CgpwaG9zcGhvX3NpZ25pZmljYW50IDwtIGZpbHRlcihwaG9zcGhvX3JhdywgYFAgdmFsdWVgIDwgcGhvc3Bob19wX3RocmVzaG9sZCkKYGBgCgojIyBWb2xjYW5vCgpgYGB7ciBwaG9zcGhvLWdyb3VwaW5nfQojIFJlcGVhdGluZyB0aGUgcHJvY2Vzc2VzIGZyb20gYmVmb3JlCiMgSWYgeW91ciBncm91cHMgYXJlIHRvbyBsYXJnZSwgdGhleSB3aWxsIG5vdCBkaXNwbGF5IGFzIGFsbCBsYWJlbGVkIGF0IG9uY2UKR3JvdXAxX3Bob3NwaG8gPC0gYygKICAiSEpVUlAiLCAiQ0hBRjFCIiwgIlJCTDEiLCAiRVlBMyIsICJCUkNBMSIsCiAgIk5DT0E2IiwgIlBBTEIyIiwgIkJBUkQxIiwgIlJFUFMyIiwgIlpORjUxMSIsICJCUldEMSIsCiAgIkwzTUJUTDMiLCAiV0RSNzYiLCAiQ0hBRjFBIiwgIlBJQVMyIiwgIlJVTlgxIiwgIlJCMSIsICJKRFAyIiwKICAiRVA0MDAiLCAiTVJHQlAiLCAiRU1TWSIsICJSWUJQIiwgIkhJUklQMyIsICJUT1BCUDEiLAogICJNR0EiLCAiUFJETTgiLCAiRVpIMiIsICJIREFDNSIsICJETUFQMSIsICJOUEFUIiwgIlNBUDEzMCIsCiAgIlNBTUQxIiwgIlpNWU5EOCIsICJBU0gxTCIsICJBUklEMUIiLCAiSERBQzQiLCAiQlJEOSIsICJOQ09SMSIsCiAgIk1DTTNBUCIsICJVVFAzIiwgIlpaRUYxIiwgIkROVFRJUDEiLCAiUFJLRDEiLCAiTVRBMSIsICJTQVRCMiIsCiAgIktBTlNMMyIsICJSRVNUIiwgIlNJUlQ2IiwgIlBIQzIiLCAiVE9QT1JTIiwgIkpNSkQxQyIsICJCUkQyIiwKICAiQVRGN0lQIiwgIlNQMSIsICJLQU5TTDEiLCAiQk1JMSIsICJDT01NRDMtQk1JMSIsICJLTVQyQyIsICJBU1hMMiIsCiAgIlRQNTNCUDEiLCAiWVkxQVAxIiwgIldBQyIsICJCQVAxIiwgIkwzTUJUTDIiLCAiWk5GNTE4QSIsICJNQkQzIiwKICAiT0JJMSIsICJET1QxTCIsICJLRE01QyIsICJSSU5HMSIsICJVU1AxNiIsICJDTk9UMyIsICJBVFhOMUwiLCAiUkNPUjMiLAogICJSQ09SMiIsICJTQ01MMiIsICJLTVQyRCIsICJBUklEMUEiLCAiR0FUQUQyQiIsICJBVE0iLCAiR1RGM0M0IiwgIlNQMTQwIiwKICAiQ0hEOCIsICJCUFRGIiwgIk1FQ1AyIiwgIkFIQ1RGMSIsICJLRE01QSIsICJNWVNNMSIsICJBVEY3SVAyIiwgIk5JUEJMIiwKICAiTFJXRDEiLCAiQVNIMkwiLCAiTUFSSzMiLCAiUlJQMUIiLCAiRVAzMDAiLCAiRUhNVDIiLCAiVE5SQzE4IiwgIk1EQzEiLAogICJSQUkxIiwgIkNJUjEiLCAiRk9YTzMiLCAiQVNYTDEiLCAiVUJFMk8iLCAiS0RNMkIiLCAiRFBGMiIsICJCUkQ0IiwKICAiTkFDQzEiLCAiQ0hENCIsICJUUklNMjgiLCAiUlVWQkwxIiwgIkFDVEw2QiIsICJEREIxIiwgIkFDVEw2QSIsCiAgIlNUSzQiLCAiQ0FSTTEiLCAiU1VNTzMiLCAiRU5ZMiIsICJDVUw1IiwgIlBIRjVBIiwgIlNJUlQyIiwgIlBLTjEiLAogICJNQVBLQVBLMiIsICJNQUdFQTEiLCAiWk5GNTE4QiIsICJTTUNIRDEiLCAiRExEIiwgIk1FTjEiLCAiSERBQzYiLAogICJEVFgzTCIsICJQU01BOCIsICJIMS02IiwgIlBNTCIsICJORUs2IiwgIlBIRjIwTDEiLCAiUkJCUDYiCikKR3JvdXAyX3Bob3NwaG8gPC0gYygKICAiRkFOQ0kiLCAiQ0RLMTQiLCAiUFJETTEiLCAiSUNBTTEiLCAiQ0RLMSIsICJSVU5YMyIsICJOT1RDSDMiLCAiQ0FTUDgiLAogICJCU1QyIiwgIkNUU0wiLCAiTVgyIiwgIkNBU1AxIiwgIlNUQVQyIiwgIk1YMSIKKQpgYGAKCmBgYHtyIHBob3NwaG8tY2F0ZWdvcmllc30KcGhvc3Bob19yYXckRGlmZkV4cCA8LSBjYXNlX3doZW4oCiAgYWJzKHBob3NwaG9fcmF3JGBsb2cyKEZDKWApID49IHBob3NwaG9fRkNfdGhyZXNob2xkICYKICAgIHBob3NwaG9fcmF3JGBQIHZhbHVlYCA+IHBob3NwaG9fcF90aHJlc2hvbGQgfiAiRkMgb25seSIsCiAgYWJzKHBob3NwaG9fcmF3JGBsb2cyKEZDKWApIDwgcGhvc3Bob19GQ190aHJlc2hvbGQgJgogICAgcGhvc3Bob19yYXckYFAgdmFsdWVgIDw9IHBob3NwaG9fcF90aHJlc2hvbGQgfiAiUCB2YWwuIG9ubHkiLAogIGFicyhwaG9zcGhvX3JhdyRgbG9nMihGQylgKSA+PSBwaG9zcGhvX0ZDX3RocmVzaG9sZCAmCiAgICBwaG9zcGhvX3JhdyRgUCB2YWx1ZWAgPD0gcGhvc3Bob19wX3RocmVzaG9sZCB+ICJGQyBhbmQgUCB2YWwuIiwKICAuZGVmYXVsdCA9ICJOUyIKKQpwaG9zcGhvX3JhdyREaWZmRXhwIDwtIGZhY3RvcihwaG9zcGhvX3JhdyREaWZmRXhwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJGQyBhbmQgUCB2YWwuIiwgIlAgdmFsLiBvbmx5IiwgIkZDIG9ubHkiLCAiTlMiKSkKCiMgQWRkaW5nIGluIGFuIGFscGhhIHZhbHVlIGR1ZSB0byBwb2ludCBzcHJlYWQgYW5kIGhpZ2hlciBoaXQgY291bnQKcGhvc3Bob19yYXckREVBbHBoYSA8LSAwLjMKcGhvc3Bob19yYXckREVBbHBoYVthYnMocGhvc3Bob19yYXckYGxvZzIoRkMpYCkgPj0gcGhvc3Bob19GQ190aHJlc2hvbGQgJgogICAgICAgICAgICAgICAgICAgcGhvc3Bob19yYXckYFAgdmFsdWVgIDw9IHBob3NwaG9fcF90aHJlc2hvbGRdIDwtIDAuOAoKCgojIFBpY2tpbmcgYSBtdWNoIHNtYWxsZXIgc3Vic2V0IG9mIHBvaW50cyBpbiBHcm91cCAxIHRvIGxhYmVsIGp1c3QgZm9yIGVhc2Ugb2Ygdmlld2luZwpHcm91cDFfcGhvc3Bob19zdWIgPC0gYygKICAiQVNIMkwiLCAiQVNYTDEiLCAiQVNYTDIiLCAiQkFQMSIsICJCTUkxIiwgIkNIRDQiLCAiRVpIMiIsICJETUFQMSIsCiAgIkVQNDAwIiwgIkdBVEFEMkIiLCAiS0FOU0wxIiwgIktBTlNMMyIsICJLTVQyQyIsICJLTVQyRCIsICJNQkQzIiwKICAiTUVOMSIsICJNR0EiLCAiTVJHQlAiLCAiTVRBMSIsICJOQ09BNiIsICJQSEMyIiwgIlBIRjIwTDEiLCAiUkVTVCIsCiAgIlJJTkcxIiwgIlJVVkJMMSIsICJTQVAxMzAiLCAiU0NNTDIiLCAiU0lSVDMiLCAiU0lSVDYiLCAiRVAzMDAiCikKcGhvc3Bob19yYXckbGFiZWxzIDwtIGlmX2Vsc2UoCiAgYWJzKHBob3NwaG9fcmF3JGBsb2cyKEZDKWApID49IHBob3NwaG9fRkNfdGhyZXNob2xkICYKICAgIHBob3NwaG9fcmF3JGBQIHZhbHVlYCA8PSBwaG9zcGhvX3BfdGhyZXNob2xkLCBwaG9zcGhvX3JhdyRTWU1CT0wyLCAiIikKYGBgCgpgYGB7ciBwaG9zcGhvLWdyb3VwZWQtdm9sY2Fuby1wbG90fQpnZ3Bsb3QocGhvc3Bob19yYXcsIGFlcyh4ID0gYGxvZzIoRkMpYCwgeSA9IC1sb2cxMChgUCB2YWx1ZWApKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gRGlmZkV4cCwgYWxwaGEgPSBERUFscGhhKSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMocGhvc3Bob19GQ190aHJlc2hvbGQsIC1waG9zcGhvX0ZDX3RocmVzaG9sZCksCiAgICAgICAgICAgICBhbHBoYSA9IDAuNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChwaG9zcGhvX3BfdGhyZXNob2xkKSwKICAgICAgICAgICAgIGFscGhhID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGxhYmVscyksIHNob3cubGVnZW5kID0gRkFMU0UsIG1heC5vdmVybGFwcyA9IDIwLAogICAgICAgICAgICAgICAgICBzaXplID0gcmVsKDMpKSArCiAgZ3VpZGVzKGFscGhhID0gIm5vbmUiKSArCiAgbGFicyh4ID0gImxvZzIoRm9sZCBDaGFuZ2UpIiwgeSA9ICItbG9nMTAoUCB2YWx1ZSkiLAogICAgICAgdGl0bGUgPSAiRmlndXJlIDIuMWE6IFZvbGNhbm8gcGxvdCIsCiAgICAgICBjb2xvciA9ICJDYXRlZ29yeSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKAogICAgYWVzdGhldGljcyA9ICJjb2xvciIsIG9wdGlvbiA9ICJtYWdtYSIsCiAgICBiZWdpbiA9IDAuMiwgZW5kID0gMC45CiAgKQpgYGAKCiMjIFBhdGh3YXkgZW5yaWNobWVudAoKYGBge3IgcGhvc3Boby1lbnJpY2h9CiMgU2VwYXJhdGUgcGhvc3Bob19zaWduaWZpY2FudCBzdWJzZXQgaW50byB1cHJlZ3VsYXRlZCB2cyBkb3ducmVndWxhdGVkCnBob3NwaG9fc2lnbmlmaWNhbnRfdXAgPC0gZmlsdGVyKHBob3NwaG9fc2lnbmlmaWNhbnQsIGBsb2cyKEZDKWAgPiAwKQpwaG9zcGhvX3NpZ25pZmljYW50X2Rvd24gPC0gZmlsdGVyKHBob3NwaG9fc2lnbmlmaWNhbnQsIGBsb2cyKEZDKWAgPCAwKQoKIyBFbnJpY2hpbmcgZm9yICp0b3RhbCogR08gdGVybXMgLSBvcHRpb25hbCwgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXplIG92ZXJhbGwgcmVzdWx0cwpwaG9zcGhvX0dPX3VwIDwtIGVucmljaEdPKHBob3NwaG9fc2lnbmlmaWNhbnRfdXAkRU5UUkVaSUQsICJvcmcuSHMuZWcuZGIiKQpwaG9zcGhvX0dPX2Rvd24gPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklELCAib3JnLkhzLmVnLmRiIikKCiMgRW5yaWNoIGZvciBLRUdHIHBhdGh3YXlzCnBob3NwaG9fS0VHR191cCA8LSBlbnJpY2hLRUdHKHBob3NwaG9fc2lnbmlmaWNhbnRfdXAkRU5UUkVaSUQpCnBob3NwaG9fS0VHR19kb3duIDwtIGVucmljaEtFR0cocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklEKQoKIyBkb3RwbG90KCkgY2FuIGJlIHVzZWQgZm9yIGFuIG92ZXJ2aWV3IG9mIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4ga2V5IHRlcm1zCmRvdHBsb3QocGhvc3Bob19LRUdHX3VwLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAyLjJhOiBQaG9zcGhvIEtFR0cgdXAiLCBsYWJlbF9mb3JtYXQgPSA1MCkgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpkb3RwbG90KHBob3NwaG9fS0VHR19kb3duLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAyLjJiOiBQaG9zcGhvIEtFR0cgZG93biIsIGxhYmVsX2Zvcm1hdCA9IDUwKSArCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmRvdHBsb3QocGhvc3Bob19HT191cCwgc2hvd0NhdGVnb3J5ID0gMjAsIGZvbnQuc2l6ZSA9IHJlbCgxKSwKICAgICAgICB0aXRsZSA9ICJGaWd1cmUgMi4yYzogUGhvc3BobyBHTyB1cCIsIGxhYmVsX2Zvcm1hdCA9IDcwKSArCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmRvdHBsb3QocGhvc3Bob19HT19kb3duLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAyLjJkOiBQaG9zcGhvIEdPIGRvd24iLCBsYWJlbF9mb3JtYXQgPSA1MCkgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCmBgYHtyIHBob3NwaG8tR08tdXAtc3BsaXR9CiMgU2VwYXJhdGluZyB0aGUgR08gdGVybXMgYnkgb250b2xvZ3kgYXNwZWN0CnBob3NwaG9fR09fdXBfTUYgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF91cCRFTlRSRVpJRCwKICAib3JnLkhzLmVnLmRiIiwgb250ID0gIk1GIgopCnBob3NwaG9fR09fdXBfQlAgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF91cCRFTlRSRVpJRCwKICAib3JnLkhzLmVnLmRiIiwgb250ID0gIkJQIgopCnBob3NwaG9fR09fdXBfQ0MgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF91cCRFTlRSRVpJRCwKICAib3JnLkhzLmVnLmRiIiwgb250ID0gIkNDIgopCmBgYAoKYGBge3IgcGhvc3Boby1HTy11cC1jbGVhbn0KIyBUYWtpbmcgdGhlIHRvcCAxMCBlbnJpY2hlZCB0ZXJtcyBpbiBlYWNoIGNhdGVnb3J5IGJ5IFAgdmFsdWUKcGhvc3Bob19HT191cF9zbGljZV9NRiA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHBob3NwaG9fR09fdXBfTUZAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKcGhvc3Bob19HT191cF9zbGljZV9CUCA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHBob3NwaG9fR09fdXBfQlBAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKcGhvc3Bob19HT191cF9zbGljZV9DQyA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHBob3NwaG9fR09fdXBfQ0NAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKCiMgQ29tYmluaW5nIHRvcCAxMCB0ZXJtcyBpbiBlYWNoIGFzcGVjdApwaG9zcGhvX0dPX3VwX3NsaWNlX0FMTCA8LSBiaW5kX3Jvd3MoCiAgIk1GIiA9IHBob3NwaG9fR09fdXBfc2xpY2VfTUYsCiAgIkJQIiA9IHBob3NwaG9fR09fdXBfc2xpY2VfQlAsCiAgIkNDIiA9IHBob3NwaG9fR09fdXBfc2xpY2VfQ0MsCiAgLmlkID0gIk9udG9sb2d5IgopCnBob3NwaG9fR09fdXBfc2xpY2VfQUxMJEdSIDwtIHBhcnNlX3JhdGlvKHBob3NwaG9fR09fdXBfc2xpY2VfQUxMJEdlbmVSYXRpbykKCiMgQWRkZWQgaW4gdG8gc2hvcnRlbiBuYW1lcyB3aGVuIHBsb3R0ZWQgLSB0aGlzIGNhbiBiZSByZW1vdmVkIG9yIGFkanVzdGVkCnBob3NwaG9fR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdW3Bob3NwaG9fR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdID09ICJveGlkb3JlZHVjdGFzZSBhY3Rpdml0eSwgYWN0aW5nIG9uIHRoZSBhbGRlaHlkZSBvciBveG8gZ3JvdXAgb2YgZG9ub3JzLCBOQUQgb3IgTkFEUCBhcyBhY2NlcHRvciJdIDwtICJveGlkb3JlZHVjdGFzZSwgYWxkZWh5ZGUvb3hvIGRvbm9yLCBOQUQvTkFEUCBhY2NlcHRvciIKCiMgUmVvcmRlcmluZyB0aGUgdG9wIHRlcm1zIGJ5IGdlbmUgcmF0aW8KcGhvc3Bob19HT191cF9zbGljZV9BTEwkRGVzY3JpcHRpb24gPC0gZmN0X3Jlb3JkZXIoCiAgcGhvc3Bob19HT191cF9zbGljZV9BTEwkRGVzY3JpcHRpb24sIHBob3NwaG9fR09fdXBfc2xpY2VfQUxMJEdSCikKYGBgCgpgYGB7ciBwaG9zcGhvLUtFR0ctdXAtZW5yaWNofQojIFJlcGVhdCBmb3IgS0VHRwojIFRha2luZyB0aGUgdG9wIDEwIGVucmljaGVkIHRlcm1zIGJ5IFAgdmFsdWUKcGhvc3Bob19LRUdHX3VwX3NsaWNlIDwtIHNsaWNlX21pbigKICBmaWx0ZXIocGhvc3Bob19LRUdHX3VwQHJlc3VsdCwgcC5hZGp1c3QgPCAwLjA1KSwKICBvcmRlcl9ieSA9IGBwLmFkanVzdGAsIG4gPSAxMAopCgojIFJlb3JkZXJpbmcgdGhlIHRvcCB0ZXJtcyBieSBnZW5lIHJhdGlvCnBob3NwaG9fS0VHR191cF9zbGljZSRHUiA8LSBwYXJzZV9yYXRpbyhwaG9zcGhvX0tFR0dfdXBfc2xpY2UkR2VuZVJhdGlvKQpwaG9zcGhvX0tFR0dfdXBfc2xpY2UkRGVzY3JpcHRpb24gPC0gZmN0X3Jlb3JkZXIoCiAgcGhvc3Bob19LRUdHX3VwX3NsaWNlJERlc2NyaXB0aW9uLAogIHBob3NwaG9fS0VHR191cF9zbGljZSRHUgopCnBob3NwaG9fS0VHR191cF9zbGljZSRPbnRvbG9neSA8LSAiS0VHRyIKYGBgCgpgYGB7ciBwaG9zcGhvLXVwLWNvbWJpbmV9CiMgQ29tYmluaW5nIEdPIGFuZCBLRUdHIGZvciB1cHJlZ3VsYXRlZCBwcm90ZWlucwpwaG9zcGhvX3VwX2JvdW5kIDwtIGJpbmRfcm93cygKICBwaG9zcGhvX0dPX3VwX3NsaWNlX0FMTCwgcGhvc3Bob19LRUdHX3VwX3NsaWNlCikKcGhvc3Bob191cF9ib3VuZCREZXNjcmlwdGlvbiA8LSBmY3RfcmVvcmRlcigKICBwaG9zcGhvX3VwX2JvdW5kJERlc2NyaXB0aW9uLCBwaG9zcGhvX3VwX2JvdW5kJEdSCikKCiMgTWFudWFsbHkgb3JkZXJpbmcgZmFjdG9ycyB0byBkaXNwbGF5IGFsbCBHTyBhc3BlY3RzIHRvZ2V0aGVyCnBob3NwaG9fdXBfYm91bmQkT250b2xvZ3kgPC0gZmFjdG9yKHBob3NwaG9fdXBfYm91bmQkT250b2xvZ3ksCiAgbGV2ZWxzID0gYygiQlAiLCAiQ0MiLCAiTUYiLCAiS0VHRyIpCikKYGBgCgpgYGB7ciBwaG9zcGhvLXVwLXBhdGh3YXlzLCBmaWcuYXNwID0gMC45fQojIEdyYXBoaW5nIGFsbCA0IGNhdGVnb3JpZXMgdG9nZXRoZXIKZ2dwbG90KAogIHBob3NwaG9fdXBfYm91bmQsCiAgIyBHZW5lIHJhdGlvIG9uIFggYXhpcywgdmVyYm9zZSBwYXRod2F5IGRlc2NyaXB0aW9uIG9uIFkgYXhpcwogICMgQ29sb3JpbmcgYnkgYWRqdXN0ZWQgUCB2YWx1ZSBhbmQgc2l6ZSBieSB0aGUgY291bnQgb2YgZ2VuZXMgaW4gYSBwYXRod2F5CiAgYWVzKHggPSBHUiwgeSA9IERlc2NyaXB0aW9uLCBjb2xvciA9IHAuYWRqdXN0LCBzaXplID0gQ291bnQpCiAgKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBsYWJlbF93cmFwKDgwKSkgKwogIGdlb21fY291bnQoKSArCiAgbGFicygKICAgIHggPSAiR2VuZSByYXRpbyIsIHkgPSAiUGF0aHdheSIsCiAgICB0aXRsZSA9ICJGaWd1cmUgMi4yZTogVXByZWd1bGF0ZWQgcGF0aHdheXMiCiAgKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKAogICAgYWVzdGhldGljcyA9ICJjb2xvciIsIG9wdGlvbiA9ICJtYWdtYSIsCiAgICBuYW1lID0gIkFkanVzdGVkIFAiLCBiZWdpbiA9IDAsIGVuZCA9IDAuOQogICkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDI1LCAyNCkpICsKICBmYWNldF9ncmlkKE9udG9sb2d5IH4gLiwKICAgIGRyb3AgPSBUUlVFLCBzcGFjZSA9ICJmcmVlX3kiLAogICAgc2NhbGVzID0gImZyZWVfeSIKICApICsKICB0aGVtZSgKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuNiwgImxpbmVzIiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEuMSkpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJsaWdodCBncmF5IiksCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk5MCIpCiAgKQpgYGAKCmBgYHtyIHBob3NwaG8tR08tZG93bi1zcGxpdH0KIyBTYW1lIHByb2Nlc3MgYXMgdGhlIHByZXZpb3VzIGNodW5rcwojIFNlcGFyYXRpbmcgdGhlIEdPIHRlcm1zIGJ5IG9udG9sb2d5IGFzcGVjdApwaG9zcGhvX0dPX2Rvd25fTUYgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLAogIG9udCA9ICJNRiIKKQpwaG9zcGhvX0dPX2Rvd25fQlAgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLAogIG9udCA9ICJCUCIKKQpwaG9zcGhvX0dPX2Rvd25fQ0MgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLAogIG9udCA9ICJDQyIKKQpgYGAKCmBgYHtyIHBob3NwaG8tR08tZG93bi1jbGVhbn0KIyBUYWtpbmcgdGhlIHRvcCAxMCBlbnJpY2hlZCB0ZXJtcyBpbiBlYWNoIGNhdGVnb3J5IGJ5IFAgdmFsdWUKcGhvc3Bob19HT19kb3duX3NsaWNlX01GIDwtIHNsaWNlX21pbigKICBmaWx0ZXIocGhvc3Bob19HT19kb3duX01GQHJlc3VsdCwgcC5hZGp1c3QgPCAwLjA1KSwKICBvcmRlcl9ieSA9IGBwLmFkanVzdGAsIG4gPSAxMAopCnBob3NwaG9fR09fZG93bl9zbGljZV9CUCA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHBob3NwaG9fR09fZG93bl9CUEByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQpwaG9zcGhvX0dPX2Rvd25fc2xpY2VfQ0MgPC0gc2xpY2VfbWluKAogIGZpbHRlcihwaG9zcGhvX0dPX2Rvd25fQ0NAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKCiMgQ29tYmluaW5nIHRvcCAxMCB0ZXJtcyBpbiBlYWNoIGFzcGVjdApwaG9zcGhvX2Rvd25fYm91bmQgPC0gYmluZF9yb3dzKAogICJNRiIgPSBwaG9zcGhvX0dPX2Rvd25fc2xpY2VfTUYsCiAgIkJQIiA9IHBob3NwaG9fR09fZG93bl9zbGljZV9CUCwKICAiQ0MiID0gcGhvc3Bob19HT19kb3duX3NsaWNlX0NDLAogIC5pZCA9ICJPbnRvbG9neSIKKQpgYGAKCmBgYHtyIHBob3NwaG8tS0VHRy1kb3duLWVucmljaH0KIyBSZXBlYXQgZm9yIEtFR0cKIyBUYWtpbmcgdGhlIHRvcCAxMCBlbnJpY2hlZCB0ZXJtcyBieSBQIHZhbHVlCnBob3NwaG9fS0VHR19kb3duX3NsaWNlIDwtIHNsaWNlX21pbigKICBmaWx0ZXIocGhvc3Bob19LRUdHX2Rvd25AcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKCiMgUmVvcmRlcmluZyB0aGUgdG9wIHRlcm1zIGJ5IGdlbmUgcmF0aW8KcGhvc3Bob19LRUdHX2Rvd25fc2xpY2UkR1IgPC0gcGFyc2VfcmF0aW8ocGhvc3Bob19LRUdHX2Rvd25fc2xpY2UkR2VuZVJhdGlvKQpwaG9zcGhvX0tFR0dfZG93bl9zbGljZSREZXNjcmlwdGlvbiA8LSBmY3RfcmVvcmRlcigKICBwaG9zcGhvX0tFR0dfZG93bl9zbGljZSREZXNjcmlwdGlvbiwKICBwaG9zcGhvX0tFR0dfZG93bl9zbGljZSRHUgopCnBob3NwaG9fS0VHR19kb3duX3NsaWNlJE9udG9sb2d5IDwtICJLRUdHIgpgYGAKCmBgYHtyIHBob3NwaG8tZG93bi1jb21iaW5lfQojIENvbWJpbmluZyBHTyBhbmQgS0VHRyBmb3IgZG93bnJlZ3VsYXRlZCBwcm90ZWlucwpwaG9zcGhvX2Rvd25fYm91bmQgPC0gYmluZF9yb3dzKAogIHBob3NwaG9fZG93bl9ib3VuZCwgcGhvc3Bob19LRUdHX2Rvd25fc2xpY2UKKQpwaG9zcGhvX2Rvd25fYm91bmQkR1IgPC0gcGFyc2VfcmF0aW8ocGhvc3Bob19kb3duX2JvdW5kJEdlbmVSYXRpbykKCnBob3NwaG9fZG93bl9ib3VuZCREZXNjcmlwdGlvbiA8LSBmY3RfcmVvcmRlcigKICBwaG9zcGhvX2Rvd25fYm91bmQkRGVzY3JpcHRpb24sIHBob3NwaG9fZG93bl9ib3VuZCRHUgopCgojIE1hbnVhbGx5IG9yZGVyaW5nIGZhY3RvcnMgdG8gZGlzcGxheSBhbGwgR08gYXNwZWN0cyB0b2dldGhlcgpwaG9zcGhvX2Rvd25fYm91bmQkT250b2xvZ3kgPC0gZmFjdG9yKHBob3NwaG9fZG93bl9ib3VuZCRPbnRvbG9neSwKICBsZXZlbHMgPSBjKCJCUCIsICJDQyIsICJNRiIsICJLRUdHIikKKQpgYGAKCmBgYHtyIHBob3NwaG8tZG93bi1wYXRod2F5cywgZmlnLmFzcCA9IDAuOX0KIyBHcmFwaGluZyBhbGwgNCBjYXRlZ29yaWVzIHRvZ2V0aGVyCmdncGxvdCgKICBwaG9zcGhvX2Rvd25fYm91bmQsCiAgIyBHZW5lIHJhdGlvIG9uIFggYXhpcywgdmVyYm9zZSBwYXRod2F5IGRlc2NyaXB0aW9uIG9uIFkgYXhpcwogICMgQ29sb3JpbmcgYnkgYWRqdXN0ZWQgUCB2YWx1ZSBhbmQgc2l6ZSBieSB0aGUgY291bnQgb2YgZ2VuZXMgaW4gYSBwYXRod2F5CiAgYWVzKHggPSBHUiwgeSA9IERlc2NyaXB0aW9uLCBjb2xvciA9IHAuYWRqdXN0LCBzaXplID0gQ291bnQpCiAgKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBsYWJlbF93cmFwKDcwKSkgKwogIGdlb21fY291bnQoKSArCiAgbGFicygKICAgIHggPSAiR2VuZSByYXRpbyIsIHkgPSAiUGF0aHdheSIsCiAgICB0aXRsZSA9ICJGaWd1cmUgMi4yZjogRG93bnJlZ3VsYXRlZCBwYXRod2F5cyIKICApICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIG5hbWUgPSAiQWRqdXN0ZWQgUCIsIGJlZ2luID0gMCwgZW5kID0gMC45CiAgKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjUsIDI0KSkgKwogIGZhY2V0X2dyaWQoT250b2xvZ3kgfiAuLAogICAgZHJvcCA9IFRSVUUsIHNwYWNlID0gImZyZWVfeSIsCiAgICBzY2FsZXMgPSAiZnJlZV95IgogICkgKwogIHRoZW1lKAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMC42LCAibGluZXMiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMS4xKSksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gImxpZ2h0IGdyYXkiKSwKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTkwIikKICApCmBgYAoKYGBge3IgZW1hcC10b3RhbCwgZmlnLmFzcCA9IDF9CnBob3NwaG9fR09fdXBfTUYgPC0gcGFpcndpc2VfdGVybXNpbShwaG9zcGhvX0dPX3VwX01GKQplbWFwcGxvdChwaG9zcGhvX0dPX3VwX01GLCBsYXlvdXQgPSAibmljZWx5Iiwgbm9kZV9sYWJlbCA9ICJub25lIikgKwogIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBzdHJfd3JhcChuYW1lLCB3aWR0aCA9IDYwKSkpICsKICBnZ3RpdGxlKCJGaWd1cmUgMi4yZzogVXByZWd1bGF0ZWQgZW5yaWNobWVudCBwbG90IChwaG9zcGhvLCBNRikiKSArCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSkpCgpwaG9zcGhvX0dPX2Rvd25fTUYgPC0gcGFpcndpc2VfdGVybXNpbShwaG9zcGhvX0dPX2Rvd25fTUYpCmVtYXBwbG90KHBob3NwaG9fR09fZG93bl9NRiwgbGF5b3V0ID0gIm5pY2VseSIsIG5vZGVfbGFiZWwgPSAibm9uZSIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gc3RyX3dyYXAobmFtZSwgd2lkdGggPSA2MCkpKSArCiAgZ2d0aXRsZSgiRmlndXJlIDIuMmg6IERvd25yZWd1bGF0ZWQgZW5yaWNobWVudCBwbG90IChwaG9zcGhvLCBNRikiKSArCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSkpCmBgYAoKIyBIaXN0b25lIHByb2Nlc3NpbmcKClRoaXMgc2VjdGlvbiBwcm9jZXNzZXMgZGF0YSBvdXRwdXQgZnJvbSBFcGlQcm9maWxlLiBXZSBjaG9vc2UgdG8gYXNzZXNzIGJvdGggdGFpbCBmcmFnbWVudHMgYW5kIGluZGl2aWR1YWwgc3VtbWVkIFBUTXMgdG8gaW5kaWNhdGUgY28tb2NjdXJyZW5jZSwgd2hpY2ggbWF5IHNpZ25pZnkgYWN0aXZpdHkgb2YgY2VydGFpbiBlcGlnZW5ldGljIGFjdG9ycy4gTm90ZSB0aGF0IHRoZSBleHBvcnRlZCBgLnhsc2AgZmlsZXMgd2lsbCBoYXZlIHRvIGJlIG9wZW5lZCBtYW51YWxseSBvbmUgdGltZSwgdGhlbiBzYXZlZCBhZ2FpbiBhcyBgLnhscyh4KWAgZm9ybWF0IGR1ZSB0byB0aGUgb3V0cHV0IGZvcm1hdC4gSW50byB0aGUgYHJhbmdlYCBhcmd1bWVudCwgcGFzcyB0aGUgY2VsbCBsb2NhdGlvbiBvZiB0aGUgZmlyc3QgcGVwdGlkZSBuYW1lICgiSDNfM184IHVubW9kIiBpbiBvdXIgZGF0YSkgdG8gc2V0IHRoZSB0b3AgbGVmdCBjb3JuZXIsIHRoZW4gKE5BLCBbdGFyZ2V0IGNvbF0pIGFzIHRoZSBzZWNvbmQgbG9jYXRpb24gdG8gc2V0IHRoZSBib3R0b20gcmlnaHQgY29ybmVyLiBUaGlzIHNlY3Rpb24gYXNzdW1lcyB0aGUgZGF0YSBpcyBzZXBhcmF0aW5nIHR3byBjb25kaXRpb25zLCBpbmZlY3RlZCBhbmQgY29udHJvbC4KCmBgYHtyIGhpc3RvbmUtaW1wb3J0fQpoaXN0b25lX3JhdGlvc19wYXRoIDwtICJ+L0RvY3VtZW50cy9SL2hpc3RvbmVfcmF0aW9zL2hpc3RvbmVfcmF0aW9zLnhscyIKaGlzdG9uZV9zaW5nbGVfcGF0aCA8LSAifi9Eb2N1bWVudHMvUi9oaXN0b25lX3JhdGlvcy9oaXN0b25lX3JhdGlvc19zaW5nbGVfUFRNcy54bHMiCgojIFRoZSBjb2x1bW5zIHdpbGwgaGF2ZSB0byBiZSBtYW51YWxseSBwYXJzZWQgLSBpbmNsdWRlIHRoZSBuYW1lcyAoZmlyc3QgY29sdW1uKSBhbmQgcmF0aW9zIGZvciBhbGwgc2FtcGxlcwpoaXN0b25lX2ZyYWdfcmF3IDwtIGRyb3BfbmEocmVhZF9leGNlbChwYXRoID0gaGlzdG9uZV9yYXRpb3NfcGF0aCwgcmFuZ2UgPSBjZWxsX2xpbWl0cyhjKDQsIDEpLCBjKE5BLCAxMSkpLCBjb2xfbmFtZXMgPSBjKCJQVE0iLCAiQzEiLCAiSTEiLCAiQzIiLCAiSTIiLCAiQzMiLCAiSTMiLCAiQzQiLCAiSTQiLCAiQzUiLCAiSTUiKSkpCgpoaXN0b25lX3NpbmdsZV9yYXcgPC0gcmVhZF9leGNlbChwYXRoID0gaGlzdG9uZV9zaW5nbGVfcGF0aCwgY29sX25hbWVzID0gYygiUFRNIiwgIkMxIiwgIkkxIiwgIkMyIiwgIkkyIiwgIkMzIiwgIkkzIiwgIkM0IiwgIkk0IiwgIkM1IiwgIkk1IiksIHJhbmdlID0gY2VsbF9saW1pdHMoYygyLCAxKSwgYyhOQSwgMTEpKSkKCiMgU2V0IHRocmVzaG9sZCB2YWx1ZXMgZm9yIHByb3Rlb21lIGFuYWx5c2lzCmhpc3RvbmVfcF90aHJlc2hvbGQgPC0gMC4wNQpoaXN0b25lX0ZDX3RocmVzaG9sZCA8LSAwLjUKYGBgCgojIyBGcmFnbWVudHMKCk5hbWUgY2xlYW51cCBpcyBub3Qgc3RyaWN0bHkgbmVjZXNzYXJ5IGJ1dCBtYWtlcyBsYWJlbHMgZWFzaWVyIHRvIHJlYWQuIFRoZXJlIG1heSBhbHNvIGJlIHNvbWUgbG9uZyBhbWlubyBhY2lkIHN0cmluZ3Mgc3VjaCBhcyAiSDEgMS0zNSBIMTIuU0VUQVBBQVBBQUFQUEFFS0FQVktLS0FBS0tBR0dUUFIiIHRoYXQgaW5kaWNhdGUgYSB1bmlxdWUgaXNvZm9ybSAoSDEuMiBpbiB0aGlzIGNhc2UpLiBUaGVzZSBjYW4gYWxzbyBiZSBtb2RpZmllZCBieSBjYWxsaW5nIGBzdHJfcmVwbGFjZV9hbGxgIGFzIGRlc2lyZWQuCgpgYGB7ciBmcmFnbWVudC1oaXN0b25lLXByb2Nlc3Npbmd9CiMgQ2xlYW5pbmcgdXAgbmFtZXMKcmF0aW9zX2ZyYWcgPC0gaGlzdG9uZV9mcmFnX3JhdwpyYXRpb3NfZnJhZyRQVE0gPC0gcmF0aW9zX2ZyYWckUFRNICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgzXzNfOCIsICJIMyAzLTgiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIM185XzE3IiwgIkgzIDktMTciKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIM18xOF8yNiIsICJIMyAxOC0yNiIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgzXzI3XzQwIiwgIkgzIDI3LTQwIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDMzXzI3XzQwIiwgIkgzLjMgMjctNDAiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIM180MV80OSIsICJIMyA0MS00OSIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgzXzU0XzYzIiwgIkgzIDU0LTYzIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDNfNzNfODMiLCAiSDMgNzMtODMiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIM18xMTdfMTI4IiwgIkgzIDExNy0xMjgiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJINF80XzE3IiwgIkg0IDQtMTciKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJINF8yMF8yMyIsICJINCAyMC0yMyIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkg0XzI0XzM1IiwgIkg0IDI0LTM1IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDRfNDBfNDUiLCAiSDQgNDAtNDUiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJINF83OV85MiIsICJINCA3OS05MiIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgxXzFfMzUiLCAiSDEgMS0zNSIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgxXzU0XzgxIiwgIkgxIDU0LTcxIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV8zNl80MiIsICJIMkExIDM2LTQyIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBM18zNl80MiIsICJIMkEzIDM2LTQyIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBWF8zNl80MiIsICJIMkFYIDM2LTQyIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV80XzExIiwgIkgyQTEgNC0xMSIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgyQUpfNF8xMSIsICJIMkFKIDQtMTEiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIMkFYXzRfMTEiLCAiSDJBWCA0LTExIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV8xXzExIiwgIkgyQTEgMS0xMSIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgyQVZfMV8xOSIsICJIMkFWIDEtMTkiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIMkFaXzFfMTkiLCAiSDJBWiAxLTE5IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV8xMl8xNyIsICJIMkExIDEyLTE3IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBM18xMl8xNyIsICJIMkEzIDEyLTE3IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV83Ml83NyIsICJIMkExIDcyLTc3IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBXzgyXzg4IiwgIkgyQSA4Mi04OCIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgyQV8xXzg4IiwgIkgyQSAxLTg4IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJCXzFfMjkiLCAiSDJCIDEtMjkiKQoKIyBHcm91cCB0d28gY29uZGl0aW9ucyB0b2dldGhlcgpyYXRpb3NfZnJhZyA8LSByYXRpb3NfZnJhZyAlPiUKICByZWxvY2F0ZShjb250YWlucygiSSIpLCAuYWZ0ZXIgPSBsYXN0X2NvbCgpKSAlPiUgCiAgcmVsb2NhdGUoIlBUTSIpCgojIENhbGN1bGF0ZSBpbmZlY3RlZC9jb250cm9sIGZvbGQgY2hhbmdlIGFuZCBQIHZhbHVlCnJhdGlvc19mcmFnIDwtIHJhdGlvc19mcmFnICU+JSAKICByb3d3aXNlKCkgJT4lIAogIG11dGF0ZShjX21lYW4gPSBtZWFuKGNfYWNyb3NzKCJDMSI6IkM1IikpLAogICAgICAgICBpX21lYW4gPSBtZWFuKGNfYWNyb3NzKCJJMSI6Ikk1IikpLAogICAgICAgICBGQyA9IGNfYWNyb3NzKGlfbWVhbikvY19hY3Jvc3MoY19tZWFuKSwKICAgICAgICAgImxvZzIoRkMpIiA9IGxvZzIoRkMpLAogICAgICAgICBpc19jb25zdGFudCA9IGFsbChjX2Fjcm9zcygiQzEiOiJDNSIpID09IGNfYWNyb3NzKCJJMSI6Ikk1IikpKQoKcmF0aW9zX2ZyYWcgPC0gY29sdW1uX3RvX3Jvd25hbWVzKHJhdGlvc19mcmFnLCB2YXIgPSAiUFRNIikKcmF0aW9zX2ZyYWckUFRNIDwtIHJvd25hbWVzKHJhdGlvc19mcmFnKQoKcmF0aW9zX2ZyYWckIlAgdmFsdWUiIDwtIE5BCnJhdGlvc19wIDwtIHJhdGlvc19mcmFnICU+JSAKICByb3d3aXNlKCkgJT4lIAogIGZpbHRlcihpc19jb25zdGFudCA9PSBGQUxTRSkgJT4lIAogICMgUmVtb3ZpbmcgYW55IHByb3RlaW5zIHRoYXQgaGF2ZSBhbGwgZXF1YWwgdmFsdWVzLCB3aGljaCBnaXZlcyBhbiAKICAjIGVycm9yIHdoZW4gcnVubmluZyB0LnRlc3QoKQogIG11dGF0ZSgiUCB2YWx1ZSIgPSB0LnRlc3QoY19hY3Jvc3MoIkMxIjoiQzUiKSwgY19hY3Jvc3MoIkkxIjoiSTUiKSwKICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIiwgdmFyLmVxdWFsID0gVFJVRSkkcC52YWx1ZSkKCnJhdGlvc19mcmFnIDwtIGZ1bGxfam9pbihyYXRpb3NfZnJhZywgcmF0aW9zX3ApCgpyYXRpb3NfZnJhZyRgbG9nMihGQylgW3JhdGlvc19mcmFnJGBsb2cyKEZDKWAgPT0gIk5hTiJdIDwtIE5BCnJhdGlvc19mcmFnJEZDW3JhdGlvc19mcmFnJEZDID09ICJOYU4iXSA8LSBOQQpgYGAKClRvIGFkZHJlc3MgaW5maW5pdGUgZm9sZCBjaGFuZ2UgdmFsdWVzIHRoYXQgYXBwZWFyIHdoZW4gYSBwcm90ZWluIGlzIG5vdCBkZXRlY3RlZCBpbiBvbmUgY29uZGl0aW9uLCB3ZSBzZXQgdGhlbSB0byBoYXZlIGEgdW5pcXVlIHNoYXBlIGluIHRoZSBmaW5hbCBwbG90IGFuZCBhc3NpZ24gYSBmb2xkIGNoYW5nZSB2YWx1ZSBlcXVhbCB0byAxLjUgdGltZXMgdGhlIG1heGltdW0gdmFsaWQgdmFsdWUuCgpgYGB7ciBmcmFnbWVudC1zZXR1cH0KIyBHZW5lcmF0aW5nIG92ZXJhbGwgdm9sY2FubyBwbG90CiMgU2V0dGluZyBjYXRlZ29yaWVzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbjoKIyBub3Qgc2lnbmlmaWNhbnQsIFAtdmFsdWUgb25seSwgRkMgb25seSwgYm90aApyYXRpb3NfZnJhZyREaWZmRXhwIDwtIGNhc2Vfd2hlbigKICBhYnMocmF0aW9zX2ZyYWckYGxvZzIoRkMpYCkgPj0gaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX2ZyYWckYFAgdmFsdWVgID4gaGlzdG9uZV9wX3RocmVzaG9sZCB+ICJGQyBvbmx5IiwKICBhYnMocmF0aW9zX2ZyYWckYGxvZzIoRkMpYCkgPCBoaXN0b25lX0ZDX3RocmVzaG9sZCAmCiAgICByYXRpb3NfZnJhZyRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCB+ICJQIHZhbC4gb25seSIsCiAgYWJzKHJhdGlvc19mcmFnJGBsb2cyKEZDKWApID49IGhpc3RvbmVfRkNfdGhyZXNob2xkICYKICAgIHJhdGlvc19mcmFnJGBQIHZhbHVlYCA8PSBoaXN0b25lX3BfdGhyZXNob2xkIH4gIkZDIGFuZCBQIHZhbC4iLAogIC5kZWZhdWx0ID0gIk5TIgopCnJhdGlvc19mcmFnJERpZmZFeHAgPC0gZmFjdG9yKHJhdGlvc19mcmFnJERpZmZFeHAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkZDIGFuZCBQIHZhbC4iLCAiUCB2YWwuIG9ubHkiLCAiRkMgb25seSIsICJOUyIpKQoKcmF0aW9zX2ZyYWckbGFiZWxzIDwtIGlmX2Vsc2UoCiAgYWJzKHJhdGlvc19mcmFnJGBsb2cyKEZDKWApID49IGhpc3RvbmVfRkNfdGhyZXNob2xkICYgCiAgICByYXRpb3NfZnJhZyRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCwgcmF0aW9zX2ZyYWckUFRNLCAiIikKCnJhdGlvc19mcmFnX3Bsb3QgPC0gcmF0aW9zX2ZyYWcgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgZmlsdGVyKGBsb2cyKEZDKWAgIT0gIi1JbmYiICYgYGxvZzIoRkMpYCAhPSAiSW5mIikKcmF0aW9zX2ZyYWdfcGxvdCRzaGFwZSA8LSAiMTYiCgpyYXRpb3NfZnJhZ19pbnZhbGlkIDwtIHJhdGlvc19mcmFnICU+JSAKICBkcm9wX25hKCkgJT4lIAogIGZpbHRlcihgbG9nMihGQylgID09ICItSW5mIiB8IGBsb2cyKEZDKWAgPT0gIkluZiIpCnJhdGlvc19mcmFnX2ludmFsaWQkYGxvZzIoRkMpYFtyYXRpb3NfZnJhZ19pbnZhbGlkJGBsb2cyKEZDKWAgPT0gIi1JbmYiXSA8LSBtaW4ocmF0aW9zX2ZyYWdfcGxvdCRgbG9nMihGQylgKSoxLjUKcmF0aW9zX2ZyYWdfaW52YWxpZCRgbG9nMihGQylgW3JhdGlvc19mcmFnX2ludmFsaWQkYGxvZzIoRkMpYCA9PSAiSW5mIl0gPC0gbWF4KHJhdGlvc19mcmFnX3Bsb3QkYGxvZzIoRkMpYCkqMS41CnJhdGlvc19mcmFnX2ludmFsaWQkc2hhcGUgPC0gIjE3IgoKcmF0aW9zX2ZyYWdfcGxvdCA8LSBmdWxsX2pvaW4ocmF0aW9zX2ZyYWdfcGxvdCwgcmF0aW9zX2ZyYWdfaW52YWxpZCkKYGBgCgpgYGB7ciBmcmFnbWVudC12b2xjYW5vLXBsb3R9CmdncGxvdChyYXRpb3NfZnJhZ19wbG90LCBhZXMoeCA9IGBsb2cyKEZDKWAsIHkgPSAtbG9nMTAoYFAgdmFsdWVgKSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IERpZmZFeHAsIHNoYXBlID0gc2hhcGUpKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gYyhtaW4ocmF0aW9zX2ZyYWdfcGxvdCRgbG9nMihGQylgKSwgbWF4KHJhdGlvc19mcmFnX3Bsb3QkYGxvZzIoRkMpYCkpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhoaXN0b25lX0ZDX3RocmVzaG9sZCwgLWhpc3RvbmVfRkNfdGhyZXNob2xkKSwKICAgICAgICAgICAgIGFscGhhID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKGhpc3RvbmVfcF90aHJlc2hvbGQpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbGFiZWxzKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgbWF4Lm92ZXJsYXBzID0gMjAsCiAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMykpICsKICBndWlkZXMoc2hhcGUgPSAibm9uZSIpICsKICBsYWJzKHggPSAibG9nMihGb2xkIENoYW5nZSkiLCB5ID0gIi1sb2cxMChQIHZhbHVlKSIsCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMy4xYTogRnJhZ21lbnQgdm9sY2FubyBwbG90IiwKICAgICAgIGNvbG9yID0gIkNhdGVnb3J5IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIGJlZ2luID0gMC4yLCBlbmQgPSAwLjkpCmBgYAoKIyMgU2luZ2xlCgpgYGB7ciBzaW5nbGUtaGlzdG9uZS1wcm9jZXNzaW5nfQojIENsZWFuaW5nIHVwIG5hbWVzCnJhdGlvc19zaW5nbGUgPC0gaGlzdG9uZV9zaW5nbGVfcmF3CnJhdGlvc19zaW5nbGUkUFRNIDwtIHJhdGlvc19zaW5nbGUkUFRNICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgzMSIsICJIMy4xIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDMzIiwgIkgzLjMiKQoKIyBHcm91cCB0d28gY29uZGl0aW9ucyB0b2dldGhlcgpyYXRpb3Nfc2luZ2xlIDwtIHJhdGlvc19zaW5nbGUgJT4lCiAgcmVsb2NhdGUoY29udGFpbnMoIkkiKSwgLmFmdGVyID0gbGFzdF9jb2woKSkgJT4lIAogIHJlbG9jYXRlKCJQVE0iKQoKIyBDYWxjdWxhdGUgaW5mZWN0ZWQvY29udHJvbCBmb2xkIGNoYW5nZSBhbmQgUCB2YWx1ZQpyYXRpb3Nfc2luZ2xlIDwtIHJhdGlvc19zaW5nbGUgJT4lIAogIHJvd3dpc2UoKSAlPiUgCiAgbXV0YXRlKGNfbWVhbiA9IG1lYW4oY19hY3Jvc3MoIkMxIjoiQzUiKSksCiAgICAgICAgIGlfbWVhbiA9IG1lYW4oY19hY3Jvc3MoIkkxIjoiSTUiKSksCiAgICAgICAgIEZDID0gY19hY3Jvc3MoaV9tZWFuKS9jX2Fjcm9zcyhjX21lYW4pLAogICAgICAgICAibG9nMihGQykiID0gbG9nMihGQyksCiAgICAgICAgICJQIHZhbHVlIiA9IHQudGVzdChjX2Fjcm9zcygiQzEiOiJDNSIpLCBjX2Fjcm9zcygiSTEiOiJJNSIpLAogICAgICAgICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLCB2YXIuZXF1YWwgPSBUUlVFKSRwLnZhbHVlKQpyYXRpb3Nfc2luZ2xlIDwtIGNvbHVtbl90b19yb3duYW1lcyhyYXRpb3Nfc2luZ2xlLCB2YXIgPSAiUFRNIikKcmF0aW9zX3NpbmdsZSRQVE0gPC0gcm93bmFtZXMocmF0aW9zX3NpbmdsZSkKCnJhdGlvc19zaW5nbGUgPC0gcmF0aW9zX3NpbmdsZSAlPiUgCiAgcm93d2lzZSgpICU+JSAKICBtdXRhdGUoY19tZWFuID0gbWVhbihjX2Fjcm9zcygiQzEiOiJDNSIpKSwKICAgICAgICAgaV9tZWFuID0gbWVhbihjX2Fjcm9zcygiSTEiOiJJNSIpKSwKICAgICAgICAgRkMgPSBjX2Fjcm9zcyhpX21lYW4pL2NfYWNyb3NzKGNfbWVhbiksCiAgICAgICAgICJsb2cyKEZDKSIgPSBsb2cyKEZDKSwKICAgICAgICAgaXNfY29uc3RhbnQgPSBhbGwoY19hY3Jvc3MoIkMxIjoiQzUiKSA9PSBjX2Fjcm9zcygiSTEiOiJJNSIpKSkKCnJhdGlvc19zaW5nbGUgPC0gY29sdW1uX3RvX3Jvd25hbWVzKHJhdGlvc19zaW5nbGUsIHZhciA9ICJQVE0iKQpyYXRpb3Nfc2luZ2xlJFBUTSA8LSByb3duYW1lcyhyYXRpb3Nfc2luZ2xlKQoKcmF0aW9zX3NpbmdsZSQiUCB2YWx1ZSIgPC0gTkEKc2luZ2xlX3AgPC0gcmF0aW9zX3NpbmdsZSAlPiUgCiAgcm93d2lzZSgpICU+JSAKICBmaWx0ZXIoaXNfY29uc3RhbnQgPT0gRkFMU0UpICU+JSAKICAjIFJlbW92aW5nIGFueSBwcm90ZWlucyB0aGF0IGhhdmUgYWxsIGVxdWFsIHZhbHVlcywgd2hpY2ggZ2l2ZXMgYW4gCiAgIyBlcnJvciB3aGVuIHJ1bm5pbmcgdC50ZXN0KCkKICBtdXRhdGUoIlAgdmFsdWUiID0gdC50ZXN0KGNfYWNyb3NzKCJDMSI6IkM1IiksIGNfYWNyb3NzKCJJMSI6Ikk1IiksCiAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIsIHZhci5lcXVhbCA9IFRSVUUpJHAudmFsdWUpCgpyYXRpb3Nfc2luZ2xlIDwtIGZ1bGxfam9pbihyYXRpb3Nfc2luZ2xlLCBzaW5nbGVfcCkKCnJhdGlvc19zaW5nbGUkYGxvZzIoRkMpYFtyYXRpb3Nfc2luZ2xlJGBsb2cyKEZDKWAgPT0gIk5hTiJdIDwtIE5BCnJhdGlvc19zaW5nbGUkRkNbcmF0aW9zX3NpbmdsZSRGQyA9PSAiTmFOIl0gPC0gTkEKYGBgCgpgYGB7ciBzaW5nbGUtc2V0dXB9CiMgR2VuZXJhdGluZyBvdmVyYWxsIHZvbGNhbm8gcGxvdCAtIHNhbWUgYXMgcHJldmlvdXMKIyBTZXR0aW5nIGNhdGVnb3JpZXMgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uOgojIG5vdCBzaWduaWZpY2FudCwgUC12YWx1ZSBvbmx5LCBGQyBvbmx5LCBib3RoCnJhdGlvc19zaW5nbGUkRGlmZkV4cCA8LSBjYXNlX3doZW4oCiAgYWJzKHJhdGlvc19zaW5nbGUkYGxvZzIoRkMpYCkgPj0gaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX3NpbmdsZSRgUCB2YWx1ZWAgPiBoaXN0b25lX3BfdGhyZXNob2xkIH4gIkZDIG9ubHkiLAogIGFicyhyYXRpb3Nfc2luZ2xlJGBsb2cyKEZDKWApIDwgaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX3NpbmdsZSRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCB+ICJQIHZhbC4gb25seSIsCiAgYWJzKHJhdGlvc19zaW5nbGUkYGxvZzIoRkMpYCkgPj0gaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX3NpbmdsZSRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCB+ICJGQyBhbmQgUCB2YWwuIiwKICAuZGVmYXVsdCA9ICJOUyIKKQpyYXRpb3Nfc2luZ2xlJERpZmZFeHAgPC0gZmFjdG9yKHJhdGlvc19zaW5nbGUkRGlmZkV4cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiRkMgYW5kIFAgdmFsLiIsICJQIHZhbC4gb25seSIsICJGQyBvbmx5IiwgIk5TIikpCgojIFNldHRpbmcgbGFiZWxzCnJhdGlvc19zaW5nbGUkbGFiZWxzIDwtIGlmX2Vsc2UoCiAgYWJzKHJhdGlvc19zaW5nbGUkYGxvZzIoRkMpYCkgPj0gaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX3NpbmdsZSRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCwgcmF0aW9zX3NpbmdsZSRQVE0sICIiKQoKcmF0aW9zX3NpbmdsZV9wbG90IDwtIHJhdGlvc19zaW5nbGUgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgZmlsdGVyKGBsb2cyKEZDKWAgIT0gIi1JbmYiICYgYGxvZzIoRkMpYCAhPSAiSW5mIikKcmF0aW9zX3NpbmdsZV9wbG90JHNoYXBlIDwtICIxNiIKCnJhdGlvc19zaW5nbGVfaW52YWxpZCA8LSByYXRpb3Nfc2luZ2xlICU+JSAKICBkcm9wX25hKCkgJT4lIAogIGZpbHRlcihgbG9nMihGQylgID09ICItSW5mIiB8IGBsb2cyKEZDKWAgPT0gIkluZiIpCnJhdGlvc19zaW5nbGVfaW52YWxpZCRgbG9nMihGQylgW3JhdGlvc19zaW5nbGVfaW52YWxpZCRgbG9nMihGQylgID09ICItSW5mIl0gPC0gbWluKHJhdGlvc19zaW5nbGVfcGxvdCRgbG9nMihGQylgKSoxLjUKcmF0aW9zX3NpbmdsZV9pbnZhbGlkJGBsb2cyKEZDKWBbcmF0aW9zX3NpbmdsZV9pbnZhbGlkJGBsb2cyKEZDKWAgPT0gIkluZiJdIDwtIG1heChyYXRpb3Nfc2luZ2xlX3Bsb3QkYGxvZzIoRkMpYCkqMS41CnJhdGlvc19zaW5nbGVfaW52YWxpZCRzaGFwZSA8LSAiMTciCgpyYXRpb3Nfc2luZ2xlX3Bsb3QgPC0gZnVsbF9qb2luKHJhdGlvc19zaW5nbGVfcGxvdCwgcmF0aW9zX3NpbmdsZV9pbnZhbGlkKQpgYGAKCmBgYHtyIHNpbmdsZS12b2xjYW5vLXBsb3R9CmdncGxvdChyYXRpb3Nfc2luZ2xlX3Bsb3QsIGFlcyh4ID0gYGxvZzIoRkMpYCwgeSA9IC1sb2cxMChgUCB2YWx1ZWApKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gRGlmZkV4cCwgc2hhcGUgPSBzaGFwZSkpICsKICBleHBhbmRfbGltaXRzKHggPSBjKG1pbihyYXRpb3Nfc2luZ2xlX3Bsb3QkYGxvZzIoRkMpYCksIG1heChyYXRpb3Nfc2luZ2xlX3Bsb3QkYGxvZzIoRkMpYCkpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhoaXN0b25lX0ZDX3RocmVzaG9sZCwgLWhpc3RvbmVfRkNfdGhyZXNob2xkKSwKICAgICAgICAgICAgIGFscGhhID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKGhpc3RvbmVfcF90aHJlc2hvbGQpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbGFiZWxzKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgbWF4Lm92ZXJsYXBzID0gMjAsCiAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMykpICsKICBndWlkZXMoc2hhcGUgPSAibm9uZSIpICsKICBsYWJzKHggPSAibG9nMihGb2xkIENoYW5nZSkiLCB5ID0gIi1sb2cxMChQIHZhbHVlKSIsCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMy4xYjogTmV0IGhpc3RvbmUgdm9sY2FubyBwbG90IiwKICAgICAgIGNvbG9yID0gIkNhdGVnb3J5IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIGJlZ2luID0gMC4yLCBlbmQgPSAwLjkKICApCmBgYAoKIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgoKSGVyZSB3ZSBiZWdpbiBhIGNvbXBhcmF0aXZlIGFuYWx5c2lzIG9mIHRoZSB0d28gbGFyZ2VyIGRhdGFzZXRzICh0b3RhbCBwcm90ZW9tZSBhbmQgcGhvc3Bob3Byb3Rlb21lKS4KCmBgYHtyIG92ZXJhbGwtdmVubiwgZmlnLmFzcCA9IDAuN30KIyBWZW5uIGRpYWdyYW0gdG8gcXVpY2tseSBsb29rIGF0IHByb3Rlb21lL3Bob3NwaG9wcm90ZW9tZSBvdmVybGFwCnZlbm5fbGlzdHMgPC0gbGlzdF90b19kYXRhX2ZyYW1lKGxpc3QoIlNldCAxIiA9IHRvdGFsX0tCJFNZTUJPTDIsICJTZXQgMiIgPSBwaG9zcGhvX3JhdyRTWU1CT0wyKSkKZ2d2ZW5uKHZlbm5fbGlzdHMsIHNldF9uYW1lX3NpemUgPSA1KSArCiAgZ2d0aXRsZSgiRmlndXJlIDRhOiBTZXQgb3ZlcmxhcCIpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCAxLCAxLCAyKSkKYGBgCgojIyBUb3RhbCBwcm90ZW9tZSBpbmZlY3RlZCB2cyBjb250cm9sCgpTZWN0aW9uIDQuMSBkaXJlY3RseSBjb21wYXJlcyB0aGUgdG90YWwgcHJvdGVvbWUgdG8gaXRzZWxmLiBIZXJlLCB3ZSBleGFtaW5lIHRoZSBzZXRzIG9mIHByb3RlaW5zIGlkZW50aWZpZWQgZXhjbHVzaXZlbHkgaW4gaW5mZWN0ZWQgY2VsbHMgKGlkZW50aWZpZWQgYnkgY29sdW1ucyB3aXRoICJKTUkiKSwgY29udHJvbCBjZWxscywgb3IgYm90aC4gRGV0ZWN0aW9uIGlucHV0cyBmb3IgdGhlc2UgY29sdW1ucyBjYW4gYmUgIkhpZ2giLCAiUGVhayBGb3VuZCIsIG9yICJuL2EiLiBUbyBpZGVudGlmeSBhIHByb3RlaW4gZm91bmQgb25seSBpbiBvbmUgY29uZGl0aW9uLCB3ZSBjaGVjayB0aGF0IGF0IGxlYXN0IG9uZSByZXBsaWNhdGUgZnJvbSB0aGF0IGNvbmRpdGlvbiBpcyBub3QgIm4vYSIuIFdlIHRoZW4gY2hlY2sgdGhhdCwgc2ltdWx0YW5lb3VzbHksIGFsbCByZXBsaWNhdGVzIGZyb20gdGhlIG90aGVyIGNvbmRpdGlvbiBhcmUgZXF1YWwgdG8gIm4vYSIsIGluZGljYXRpbmcgdGhlIHByb3RlaW4gd2FzIG5vdCBmb3VuZC4gSWYgYm90aCByZXF1aXJlbWVudHMgYXJlIG1ldCwgYSBzdHJpbmcgaXMgbWFwcGVkIHRvIHRoYXQgcHJvdGVpbiBpbmRpY2F0aW5nIGl0cyBwcmVzZW5jZS4gSWYgYSBwcm90ZWluIGhhcyBhdCBsZWFzdCBvbmUgcmVwbGljYXRlIG5vdCBlcXVhbCB0byAibi9hIiBpbiBib3RoIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zLCAiQWxsIiBpcyBhc3NpZ25lZCwgaW5kaWNhdGluZyBvdmVybGFwLgoKYGBge3IgY29uZGl0aW9uLWNhc2VzfQojIElkZW50aWZ5IHByb3RlaW5zIGV4cHJlc3NlZCBpbiBib3RoIGNvbmRpdGlvbnMsIG9ubHkgY29udHJvbCwgb3Igb25seSBpbmZlY3RlZC4KdG90YWxfZGlmZl92YWxpZCA8LSB0b3RhbF9LQiAlPiUgIG11dGF0ZShgRGlmZmVyZW50aWFsP2AgPSBjYXNlX3doZW4oCiAgIyBDb250cm9sIE9OTFkKICAjIEJDRyBlbXB0eSwgYXQgbGVhc3QgMSBjdHJsIG5vdCBlbXB0eQogICgoKGBGb3VuZCBpbiBTYW1wbGU6IEYxOiBTYW1wbGUsIENvbnRyb2xgICE9ICJuL2EiKSB8CiAgICAgIChgRm91bmQgaW4gU2FtcGxlOiBGMjogU2FtcGxlLCBDb250cm9sYCAhPSAibi9hIikgfAogICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjM6IFNhbXBsZSwgQ29udHJvbGAgIT0gIm4vYSIpKSAmCiAgICAgKChgRm91bmQgaW4gU2FtcGxlOiBGNDogU2FtcGxlLCBKTUlgID09ICJuL2EiKSAmCiAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEY1OiBTYW1wbGUsIEpNSWAgPT0gIm4vYSIpICYKICAgICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjU6IFNhbXBsZSwgSk1JYCA9PSAibi9hIikgJgogICAgICAgIChgRm91bmQgaW4gU2FtcGxlOiBGNjogU2FtcGxlLCBKTUlgID09ICJuL2EiKSAmCiAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEY3OiBTYW1wbGUsIEpNSWAgPT0gIm4vYSIpICYKICAgICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjg6IFNhbXBsZSwgSk1JYCA9PSAibi9hIikpKSB+ICJDb250cm9sIG9ubHkiLAogICMgSW5mZWN0ZWQgT05MWQogICMgQ29udHJvbCBlbXB0eSwgYXQgbGVhc3Qgb25lIEJDRyBub3QgZW1wdHkKICAgICgoKGBGb3VuZCBpbiBTYW1wbGU6IEYxOiBTYW1wbGUsIENvbnRyb2xgID09ICJuL2EiKSAmCiAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEYyOiBTYW1wbGUsIENvbnRyb2xgID09ICJuL2EiKSAmCiAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEYzOiBTYW1wbGUsIENvbnRyb2xgID09ICJuL2EiKSkgJgogICAgICAgKChgRm91bmQgaW4gU2FtcGxlOiBGNDogU2FtcGxlLCBKTUlgICE9ICJuL2EiKSB8CiAgICAgICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjU6IFNhbXBsZSwgSk1JYCAhPSAibi9hIikgfAogICAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEY1OiBTYW1wbGUsIEpNSWAgIT0gIm4vYSIpIHwKICAgICAgICAgIChgRm91bmQgaW4gU2FtcGxlOiBGNjogU2FtcGxlLCBKTUlgICE9ICJuL2EiKSB8CiAgICAgICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjc6IFNhbXBsZSwgSk1JYCAhPSAibi9hIikgfAogICAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEY4OiBTYW1wbGUsIEpNSWAgIT0gIm4vYSIpKSkgfiAiQkNHIG9ubHkiLAogICAgIyBCb3RoCiAgICAuZGVmYXVsdCA9ICJBbGwiKSkKYGBgCgpFdWxlciBwbG90cyBhcmUgc2l6ZWQgcHJvcG9ydGlvbmFsbHkgdG8gdGhlaXIgZ3JvdXBzIGFuZCBhcmUgbW9yZSBmcmllbmRseSB0byBtdWx0aXBsZSBzZXRzLiBUbyBnZW5lcmF0ZSB0aGUgRXVsZXIgcGxvdHMsIHRoZSBpbnB1dCBuZWVkcyB0byBiZSBhIHNpbmdsZSBjb2x1bW4gY29udGFpbmluZyBhbGwgcGVwdGlkZXMgZm91bmQgaW4gdGhhdCBjb25kaXRpb24uIFRoZSBgcHJpbnRgIGNvbW1hbmQgcHJvdmlkZXMgc3VtbWFyeSBzdGF0aXN0aWNzIHRvIGVuc3VyZSB0aGF0IHRoZSBFdWxlciBwbG90IGRvZXMgbm90IG1pc3JlcHJlc2VudCB0aGUgZGF0YS4KCmBgYHtyIGV1bGVyLWxpc3RzfQojIFBpdm90aW5nIHRvIG1ha2UgYSB3aWRlciB0YWJsZQp0b3RhbF93aWRlcl92YWxpZCA8LSBwaXZvdF93aWRlcih0b3RhbF9kaWZmX3ZhbGlkLAogIG5hbWVzX2Zyb20gPSBgRGlmZmVyZW50aWFsP2AsIHZhbHVlc19mcm9tID0gYFNZTUJPTDJgKQogIAojIENyZWF0ZSBhIGxpc3Qgb2YgaW5mZWN0aW9uIG9ubHkgcGVwdGlkZXMsIHJlbW92aW5nIGVtcHR5IHJvd3MgKE5BKQpCQ0dfZGlzdF92YWxpZCA8LSB0b3RhbF93aWRlcl92YWxpZCAlPiUgZGlzdGluY3QoYEJDRyBvbmx5YCkgJT4lIGRyb3BfbmEoKQpjb2xuYW1lcyhCQ0dfZGlzdF92YWxpZCkgPC0gYygiUGVwdGlkZXMiKQoKIyBDcmVhdGUgYSBsaXN0IG9mIGNvbnRyb2wgb25seSBwZXB0aWRlcywgcmVtb3ZpbmcgZW1wdHkgcm93cyAoTkEpCkN0cmxfZGlzdF92YWxpZCA8LSB0b3RhbF93aWRlcl92YWxpZCAlPiUgZGlzdGluY3QoYENvbnRyb2wgb25seWApICU+JSBkcm9wX25hKCkKY29sbmFtZXMoQ3RybF9kaXN0X3ZhbGlkKSA8LSBjKCJQZXB0aWRlcyIpCgojIENyZWF0ZSBhIGxpc3Qgb2YgcGVwdGlkZXMgcHJlc2VudCBpbiBib3RoLCByZW1vdmluZyBlbXB0eSByb3dzIChOQSkKQWxsX2Rpc3RfdmFsaWQgPC0gdG90YWxfd2lkZXJfdmFsaWQgJT4lIGRpc3RpbmN0KGBBbGxgKSAlPiUgZHJvcF9uYSgpCmNvbG5hbWVzKEFsbF9kaXN0X3ZhbGlkKSA8LSBjKCJQZXB0aWRlcyIpCgojIENyZWF0ZSBhIGxpc3Qgb2YgQkNHICsgYm90aApCQ0dfYm90aF9kaXN0X3ZhbGlkIDwtIGFkZF9yb3coQkNHX2Rpc3RfdmFsaWQsIEFsbF9kaXN0X3ZhbGlkKSAlPiUgZGlzdGluY3QoKQpjb2xuYW1lcyhCQ0dfYm90aF9kaXN0X3ZhbGlkKSA8LSBjKCJJbmZlY3RlZCIpCgojIENyZWF0ZSBhIGxpc3Qgb2YgY29udHJvbCArIGJvdGgKQ3RybF9ib3RoX2Rpc3RfdmFsaWQgPC0gYWRkX3JvdyhDdHJsX2Rpc3RfdmFsaWQsIEFsbF9kaXN0X3ZhbGlkKSAlPiUgZGlzdGluY3QoKQpjb2xuYW1lcyhDdHJsX2JvdGhfZGlzdF92YWxpZCkgPC0gYygiQ29udHJvbCIpCgojIEdlbmVyYXRlIHBsb3QKZml0X3RvdGFsX3ZhbGlkIDwtIGV1bGVyKGMoQkNHX2JvdGhfZGlzdF92YWxpZCwgQ3RybF9ib3RoX2Rpc3RfdmFsaWQpKQpwbG90KGZpdF90b3RhbF92YWxpZCwKICBxdWFudGl0aWVzID0gVFJVRSwKICBmaWxscyA9IGMoInBhbGV2aW9sZXRyZWQxIiwgImNvcm5mbG93ZXJibHVlIiwgImdhaW5zYm9ybyIpLAogIG1haW4gPSAiRmlndXJlIDQuMWE6IENvbmRpdGlvbiBFdWxlciBwbG90IgopCmBgYAoKYGBge3J9CnByaW50KGZpdF90b3RhbF92YWxpZCkKYGBgCgojIERpc2Vhc2UgZW5yaWNobWVudAoKVG8gY29tcGxldGUgdGhlIGludGVncmF0ZWQgYW5hbHlzaXMsIHdlIGV4YW1pbmUgYSBnZW5lLWRpc2Vhc2UgYXNzb2NpYXRpb24gKEdEQSkgc3VtbWFyeSBmcm9tIERpc0dlTkVULiBUaGUgR0RBIEV4Y2VsIHNoZWV0cyBjYW4gYmUgZG93bmxvYWRlZCBkaXJlY3RseSBmcm9tIHRoZSB3ZWJzaXRlIGFmdGVyIGlkZW50aWZ5aW5nIHRoZSBkaXNlYXNlIG9mIGludGVyZXN0LiBQb2ludCB0aGUgYHJlYWRfeGxzeGAgY2FsbCB0byB0aGlzIGZpbGUuCgpgYGB7ciBHREEsIGZpZy5hc3AgPSAxfQpCQ194bCA8LSByZWFkX3hsc3goIn4vRG9jdW1lbnRzL1IvRmluYWwgYW5hbHlzaXMvQzIxNDU0NzJfZGlzZWFzZV9nZGFfc3VtbWFyeS54bHN4IikKCiMgR2V0IGxpc3RzIG9mIHByb3RlaW5zIHRoYXQgd2VyZSBzaWduaWZpY2FudCBBTkQgYWJvdmUgdGhlIEZDIHRocmVzaG9sZAp0b3RhbF9GQyA8LSBmaWx0ZXIodG90YWxfc2lnbmlmaWNhbnQsIGFicyhgbG9nMihGQylgKSA+IHRvdGFsX0ZDX3RocmVzaG9sZCkKcGhvc3Bob19GQyA8LSBmaWx0ZXIocGhvc3Bob19zaWduaWZpY2FudCwgYWJzKGBsb2cyKEZDKWApID4gcGhvc3Bob19GQ190aHJlc2hvbGQpCgojIEdlbmVyYXRlIHBsb3QKZ2d2ZW5uKGxpc3QoCiAgIk5NSUJDIiA9IEJDX3hsJEdlbmUsCiAgIlRvdGFsIHByb3Rlb21lIiA9IHRvdGFsX0ZDJFNZTUJPTDIsCiAgIlBob3NwaG9wcm90ZW9tZSIgPSBwaG9zcGhvX0ZDJFNZTUJPTDIKKSkgKyBnZ3RpdGxlKCJGaWd1cmUgNWE6IFZlbm4gZGlhZ3JhbSIpICsgdGhlbWUoCiAgcGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwKICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKZml0X0JDIDwtIGV1bGVyKGxpc3QoCiAgIk5NSUJDIiA9IEJDX3hsJEdlbmUsCiAgIlRvdGFsIHByb3Rlb21lIiA9IHRvdGFsX0ZDJFNZTUJPTDIsCiAgIlBob3NwaG9wcm90ZW9tZSIgPSBwaG9zcGhvX0ZDJFNZTUJPTDIKKSkKcGxvdChmaXRfQkMsIHF1YW50aXRpZXMgPSBUUlVFLCBtYWluID0gIkZpZ3VyZSA1YTogR0RBIEV1bGVyIHBsb3QiKQpgYGAKCmBgYHtyfQpwcmludChsaXN0X3RvX2RhdGFfZnJhbWUoZml0X0JDKSkKYGBgCgpGcm9tIHRoaXMgdGFibGUsIHRoZXJlIGlzIGEgbWlzc2luZyBvdmVybGFwIHJlZ2lvbiBpbiBhbGwgdGhyZWUgc2V0cyB0aGF0IHRoZSBFdWxlciBkaWFncmFtIGNvdWxkIG5vdCByZXNvbHZlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIG5vbi16ZXJvIHJlc2lkdWFscy4gSW4gdGhpcyBjYXNlLCB0aGUgVmVubiBkaWFncmFtIGlzIHRoZSBiZXR0ZXIgY2hvaWNlLgoKYGBge3IgR0RBLWxpc3QsIHJlc3VsdHMgPSAiYXNpcyJ9CiMgUHJpbnQgdGhlIGxpc3Qgb2YgcHJvdGVpbnMgaW4gZWFjaCBpbnRlcnNlY3Rpb24KY2F0KHBhc3RlKCIqKk5NSUJDL1Bob3NwaG86KiogIiwgcGFzdGUoaW50ZXJzZWN0KEJDX3hsJEdlbmUsIHBob3NwaG9fRkMkU1lNQk9MMiksIGNvbGxhcHNlID0gIiwgIikpKQpjYXQocGFzdGUoIioqTk1JQkMvVG90YWw6KiogIiwgcGFzdGUoaW50ZXJzZWN0KEJDX3hsJEdlbmUsIHRvdGFsX0ZDJFNZTUJPTDIpLCBjb2xsYXBzZSA9ICIsICIpKSkKY2F0KHBhc3RlKCIqKlRvdGFsL1Bob3NwaG86KiogIiwgcGFzdGUoaW50ZXJzZWN0KHRvdGFsX0ZDJFNZTUJPTDIsIHBob3NwaG9fRkMkU1lNQk9MMiksIGNvbGxhcHNlID0gIiwgIikpKQpjYXQocGFzdGUoIioqTk1JQkMvUGhvc3Boby9Ub3RhbDoqKiAiLCBwYXN0ZShpbnRlcnNlY3QoaW50ZXJzZWN0KEJDX3hsJEdlbmUsIHBob3NwaG9fRkMkU1lNQk9MMiksIHRvdGFsX0ZDJFNZTUJPTDIpLCBjb2xsYXBzZSA9ICIsICIpKSkKYGBg